如何解决不是tar文件的任何错误

111 阅读2分钟

Python标准库的tarfile 模块有一个tarfile.is_tarfile 函数来告诉你某个文件是否是一个 tar 文件,或者至少是一个模块可以读取的 tar 文件。在Python中并不傻,它的操作方式是试图用以下方式打开文件tarfile.open;如果open() 成功了,显然这是一个好的 tar 文件。

不幸的是,这也许是一个错误,它不能报告各种实际上不是tar文件的东西的任何错误。在Unix系统中,这个问题的最简单的再现是:

>>> import tarfile
>>> tarfile.open("/dev/zero", "r:")

这不会引起任何异常,并给你一个 TarFile 对象,它将报告你有一个空的 tar 文件。

(如果你去掉了'r:',这就会挂掉,最终是因为lzma 模块会很高兴地从一个零字节的数据流中永远读取。除非你告诉它,否则 tarfile 模块通常会在你潜在的 tar 文件上尝试一系列的解压程序,包括 lzma 对.xz文件。)

一个会导致这个问题的具体形式是任何名义上的 "tar文件",它以512字节的零字节开始(在任何解压应用之后)。既然这适用于/dev/zero ,我们就有了我们方便且明显不正确的再现案例。可能还有其他初始512字节的块会导致这种情况;我还没有深入调查代码,部分原因是它很纠结。

我怀疑这是在TarFile.next 函数中的一个错误,它看起来缺少一个 'elif self.offset == 0:' 子句(见这里开始的代码块)。但不管这个问题是否是一个bug,是否会在未来的Python 3版本中被修复,它在现有的Python版本中都是非常普遍的,所以任何关心这个问题的代码(我们有一些)都需要应对它。

我目前的黑客解决方法是检查返回的 TarFile 对象上的.members列表是否为空。这不是一个记录在案的属性,但它不太可能改变,而且今天还能用(而且感觉比检查.firstmember 是否为None 稍微不那么肮脏)。

(由于超出这篇文章范围的原因,我决定努力寻找如何提交Python的bug报告,找出我上次向他们的问题跟踪器提交bug报告时的登录名,并将这个版本作为issue 36596提交)。