Watch at Ease看见世界,读懂生活。

科技 · 实用指南

dircmp.diff_files一直为空?刷新两处结果,再检查全部共同文件

28项本地检查说明:clear_cache()、新建默认dircmp和深查diff_files,各自都可能漏掉内容不同的文件。

原创说明图:同签名改写后,模块比较缓存与旧dircmp列表分别保留结果。
原创示意图:原创说明图:实测CPython 3.12.14;清模块缓存不会刷新旧对象已经算出的列表。 Watch at Ease

dircmp.diff_files为空,不能证明两个目录的文件字节相同。在CPython 3.12.14实验中,我们把一个四字节文件从AAAA改成BBBB,并故意保留大小和修改时间。浅比较以及此前缓存过的深比较都仍返回True。

清除filecmp模块缓存后,新一次深比较返回False,但旧dircmp对象已经算出的空列表没有更新。对这个空diff_files列表做深比较,实际检查了零个文件名。修复要区分这两处状态,并按明确规则检查当前全部共同文件。

1. 区分两处保留结果

filecmp.cmp的比较签名包括文件类型、大小和修改时间。shallow=True时,相同签名可以直接被接受,无须读取字节。shallow=False跳过这种捷径,但路径和签名未变时,文档所述的比较缓存仍可能提供此前结果。

dircmp还有另一种生命周期:属性在首次需要时延迟计算,然后保留在该对象上。我们先计算d0.diff_files,再改写文件。即使clear_cache()之后新深比较已返回False,d0.diff_files仍是原来的空列表;清模块缓存没有重建d0。

2. 用受控改写复现问题

在自己拥有的新临时目录中,两边都写入内容为b"AAAA"的report.bin,并设相同的固定修改时间。执行clear_cache()、cmp(a,b,shallow=False),再建立d0=filecmp.dircmp(left,right)并读取d0.diff_files。初始深比较为True,列表为空。

只把右侧改为b"BBBB",再用os.utime恢复原mtime。断言确认类型、大小、mtime签名相同,而实际字节不同。默认浅比较返回True,此前缓存过的深比较也返回True。这里是刻意保留签名,不是测量文件系统时间戳精度。

3. 重建对象,比较common_files

先停止或隔离写入者,让输入保持稳定。实测3.12可用:filecmp.clear_cache(); fresh=filecmp.dircmp(left,right,ignore=[],hide=[]); match,mismatch,errors=filecmp.cmpfiles(left,right,fresh.common_files,shallow=False)。显式空过滤列表是小型夹具的规则,不是通用生产建议。

改写后的夹具中,match和errors为空,mismatch包含report.bin。相反,把fresh.diff_files交给cmpfiles会得到三个空列表:新的默认对象仍做浅比较,已经漏掉被误判为相同的文件。内容候选集应取common_files,同时保留left_only、right_only、common_funny和errors,不能只输出一个成功标志。

4. 使用新参数前先核对版本

实际3.12.14解释器对filecmp.dircmp(left,right,shallow=False)抛出TypeError。Python 3.13文档说明该参数从3.13加入;我们没有运行3.13。兼容版本提供公开的深比较选项,但不能因此把可变输入当成原子快照,也不能承诺刷新已求值的旧对象。

默认dircmp在不满足元数据捷径时也会读取内容。最终对照中,两边字节相同、修改时间不同,浅比较仍返回True。因此浅比较并非任何情况下都只看元数据,时间戳不同也不能单独证明内容不同。

5. 把错误和名称过滤纳入结果

missing.bin对照进入cmpfiles的errors,而非mismatch。新增extra.bin后,旧对象已经算出的right_only没有它,新对象有它。另一个在新增前创建、尚未访问列表的dircmp,在首次访问时看到了它:构造对象的时刻不是快照边界。

临时夹具中名为.git的普通文件被默认ignore规则隐藏,使用ignore=[],hide=[]后出现。先决定应用要比较哪些名称。内容差异为空,不能证明名称、类型、遍历范围或权限都相同。本实验只涉及平面普通文件,没有测试递归、链接遍历、特殊文件或并发改写。

6. 用独立字节检查完成核验

本次实际安装的GNU cmp为3.10。对两个已改写report.bin运行cmp -s返回1,独立确认字节不同;GNU上游手册定义0为相同、1为不同、2为执行遇到问题。重新计算的SHA-256也不同。错误退出不能当成相同或普通内容差异。

全部28项断言在CPython 3.12.14/Linux通过。报告保留运行版本、平台、GNU版本、脚本SHA和结果。实际审计应从稳定输入集报告选定名称范围、每个共同文件的比较结果和未解决错误。字节相等只是内容观察;真实性、元数据一致和写入期间的一致性需要另外的保证。

原创说明图:空diff_files重查与全部common_files深比较的结果,并列GNU cmp退出码。
原创示意图:原创说明图:检查全部共同文件才能发现夹具差异;名称范围和错误仍须单独检查。 Watch at Ease