鉴于功能
def get_files_from_sha(sha, files):
from subprocess import Popen, PIPE
import tarfile
if 0 == len(files):
return {}
p = Popen(["git", "archive", sha], bufsize=10240, stdin=PIPE, stdout=PIPE, stderr=PIPE)
tar = tarfile.open(fileobj=p.stdout, mode='r|')
p.communicate()
contents = {}
doall = files == '*'
if not doall:
files = set(files)
for entry in tar:
if (isinstance(files, set) and entry.name in files) or doall:
tf = tar.extractfile(entry)
contents[entry.name] = tf.read()
if not doall:
files.discard(entry.name)
if not doall:
for fname in files:
contents[fname] = None
tar.close()
return contents
在一个循环中为sha
的某些值调用,经过一段时间(在我的情况下,4次迭代),它在调用tf.read()
时开始失败,并显示消息:
Traceback (most recent call last):
File "../yap-analysis/extract.py", line 243, in <module>
commits, identities, identities_by_name, identities_by_email, identities_freq = build_commits(commits)
File "../yap-analysis/extract.py", line 186, in build_commits
commit = get_commit(commit)
File "../yap-analysis/extract.py", line 84, in get_commit
contents = get_files_from_sha(commit['sha'], files)
File "../yap-analysis/extract.py", line 42, in get_files_from_sha
contents[entry.name] = tf.read()
File "/usr/lib/python2.7/tarfile.py", line 817, in read
buf += self.fileobj.read()
File "/usr/lib/python2.7/tarfile.py", line 737, in read
return self.readnormal(size)
File "/usr/lib/python2.7/tarfile.py", line 746, in readnormal
return self.fileobj.read(size)
File "/usr/lib/python2.7/tarfile.py", line 573, in read
buf = self._read(size)
File "/usr/lib/python2.7/tarfile.py", line 581, in _read
return self.__read(size)
File "/usr/lib/python2.7/tarfile.py", line 606, in __read
buf = self.fileobj.read(self.bufsize)
ValueError: I/O operation on closed file
我怀疑子进程尝试进行某些并行化(?)。
实际原因是什么以及如何在python2上以干净和健壮的方式解决它?
答案 0 :(得分:1)
我认为您的问题是p.communicate()
。此方法发送到stdin,从stdout和stderr(您没有捕获)读取并等待进程终止。
tarfile
正试图从进程stdout中读取,到时它就完成了进程,因此错误。
我还没有尝试运行您的代码(我无法访问git
),但您可能根本不需要p.communicate
,请尝试将其评论出来。
答案 1 :(得分:1)
不要在.communicate()
实例上使用Popen
;在完成之前,它会读取stdout
流。来自文档:
与流程交互:将数据发送到stdin。从stdout和stderr读取数据,直到达到文件结尾。
.communicate()
的代码甚至会在管道的.close()
上添加明确的stdout
调用。
只需删除对.communicate()
的调用就足够了,但在读取tarfile内容之后还要添加.wait()
:
tar.close()
p.stdout.close()
p.wait()
可能tar.close()
也关闭p.stdout
,但额外的.close()
不应该受到伤害。