用子流程,IPC,SMP关闭fd

时间:2012-12-02 11:32:45

标签: python python-2.7 ipc

鉴于功能

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上以干净和健壮的方式解决它?

2 个答案:

答案 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()不应该受到伤害。