NamedTemporaryFile 存在,但外部程序无法访问它

NamedTemporaryFile exists, but external program can't access it

这是 关于使用 NamedTemporaryFile()

的后续分类

我有一个创建和写入临时文件的功能。然后我想在另一个函数中使用该文件,该函数调用使用该文件的终端命令(该程序来自 Blast+ 套件,blastn)。

def db_cds_to_fna(collection="genes"):  # collection gets data from mongoDB

    tmp_file = NamedTemporaryFile()
    # write stuff to file

    return tmp_file

def blast_all(blast_db, collection="genes"):        

    tmp_results = NamedTemporaryFile()    
    db_fna = db_cds_to_fna(collection) # should return another file object

    Popen(
        ['blastn',
         '-query', db_fna.name,
         '-db', blast_db,
         '-out', tmp_results.name,
         '-outfmt', '5']  # xml output
    )

    return tmp_results

当我调用 blast_all 时,我收到来自 blastn 命令的错误:

Command line argument error: Argument "query". File is not accessible:  `/var/folders/mv/w3flyjvn7vnbllysvzrf9y480000gn/T/tmpAJVWoz'

但是,就在 Popen 调用之前,如果我执行 os.path.isfile(db_fna.name),它的计算结果为 True。我也可以

print Popen(['head', db_fna.name]).communicate(0)

它正确地吐出文件的第一行。所以文件存在,并且是可读的。此外,我使用相同的策略从同一个 blast+ 套件调用不同的程序(makeblastdb,请参阅顶部链接的问题)并且它有效。权限可能有问题吗? FWIW blastn returns 如果文件不存在,同样的错误,但很明显我正在正确创建文件并且当我进行 Popen 调用时它是可读的,所以我我难住了。

我曾经遇到过非常相似的问题。我找了好久,以为我永远找不到原因。

就我而言,问题是由于文件系统延迟造成的。我想我最终使用 time.sleep 进行了一个肮脏的黑客攻击,以便在开始在子进程中访问它之前给文件系统一些时间来创建临时文件。

希望对您有所帮助!

我相信我弄清楚了导致这种行为的原因。首先,Popen() 函数通常不会等到外部命令完成后再继续执行。其次,因为正如用户 glibdud 在

中提到的 to my other question, NamedTemporaryFile acts like TemporaryFile

It will be destroyed as soon as it is closed (including an implicit close when the object is garbage collected).

因为我的 blast_all() 函数的末尾没有 return query 临时文件,所以当外部 blastn 命令是 运行ning,所以文件被删除了。我猜外部 head 命令运行得如此之快,它不会遇到这个问题,但是 blastn 最多可能需要几分钟才能到达 运行.

所以解决方案是强制Popen()等待:

Popen(
    ['blastn',
     '-query', db_fna.name,
     '-db', blast_db,
     '-out', tmp_results.name,
     '-outfmt', '5']  # xml output
).wait()

我认为问题可能是 OS 没有将文件同步到磁盘。写入文件描述符后执行:

tmp_file.flush()
os.fsync(tmp_file)

https://docs.python.org/3/library/os.html#os.fsync