如何创建可由子进程读取的临时文件



我正在编写一个Python脚本,它需要将一些数据写入临时文件,然后创建一个运行c++程序的子进程,该子进程将读取临时文件。我试图使用NamedTemporaryFile为此,但根据文档,

在命名的临时文件仍然打开的情况下,是否可以使用该名称第二次打开文件,这在不同的平台上是不同的(它可以在Unix上使用;

事实上,在Windows上,如果我在写入临时文件后刷新它,但直到我想要它消失时才关闭它,子进程无法打开它进行读取。

我正在通过使用delete=False创建文件来解决这个问题,在生成子进程之前关闭它,然后在我完成后手动删除它:

fileTemp = tempfile.NamedTemporaryFile(delete = False)
try:
    fileTemp.write(someStuff)
    fileTemp.close()
    # ...run the subprocess and wait for it to complete...
finally:
    os.remove(fileTemp.name)

这看起来不太优雅。有更好的方法吗?也许有一种方法可以打开临时文件的权限,以便子进程可以访问它?

既然没有人愿意公开这些信息……

tempfile确实暴露了一个函数mkdtemp(),它可以简化这个问题:

try:
    temp_dir = mkdtemp()
    temp_file = make_a_file_in_a_dir(temp_dir)
    do_your_subprocess_stuff(temp_file)
    remove_your_temp_file(temp_file)
finally:
    os.rmdir(temp_dir)

我把中间函数的实现留给读者,因为有人可能希望做一些事情,比如使用mkstemp()来加强临时文件本身的安全性,或者在删除文件之前覆盖它。我不太清楚通过仔细阅读tempfile的源代码,可能会有哪些不容易规划的安全限制。

无论如何,是的,在Windows上使用NamedTemporaryFile可能是不优雅的,我在这里的解决方案也可能是不优雅的,但你已经决定Windows支持比优雅的代码更重要,所以你不妨继续做一些可读的

根据Richard Oudkerk

(…)是试图重新打开NamedTemporaryFile失败的唯一原因Windows是因为当我们重新打开时我们需要使用O_TEMPORARY

,他给出了一个如何在Python 3.3+

中做到这一点的例子
import os, tempfile
DATA = b"hello bob"
def temp_opener(name, flag, mode=0o777):
    return os.open(name, flag | os.O_TEMPORARY, mode)
with tempfile.NamedTemporaryFile() as f:
    f.write(DATA)
    f.flush()
    with open(f.name, "rb", opener=temp_opener) as f:
        assert f.read() == DATA
assert not os.path.exists(f.name)

因为Python 2中内置的open()中没有opener参数。x,我们必须结合较低级别的os.open()os.fdopen()函数来达到同样的效果:

import subprocess
import tempfile
DATA = b"hello bob"
with tempfile.NamedTemporaryFile() as f:
    f.write(DATA)
    f.flush()
    subprocess_code = 
    """import os
       f = os.fdopen(os.open(r'{FILENAME}', os.O_RDWR | os.O_BINARY | os.O_TEMPORARY), 'rb')
       assert f.read() == b'{DATA}'
    """.replace('n', ';').format(FILENAME=f.name, DATA=DATA)
    subprocess.check_output(['python', '-c', subprocess_code]) == DATA

你总是可以去低级别的,虽然我不确定它对你来说是否足够干净:

fd, filename = tempfile.mkstemp()
try:
    os.write(fd, someStuff)
    os.close(fd)
    # ...run the subprocess and wait for it to complete...
finally:
    os.remove(filename)

至少如果您使用现有的Python库打开临时文件,在Windows的情况下,从多个进程访问它是不可能的。根据MSDN,您可以指定第三个参数(dwSharedMode)共享模式标志FILE_SHARE_READCreateFile()函数,其:

允许对请求读取的文件或设备进行后续打开操作访问。否则,其他进程无法打开该文件或设备它们请求读访问。如果未指定此标志,则文件或设备已打开以进行读访问,此功能失败。

所以,你可以写一个Windows特定的C例程来创建一个自定义的临时文件打开器函数,从Python调用它,然后你可以让你的子进程访问文件而不会出现任何错误。但我认为你应该坚持你现有的方法,因为它是最便携的版本,可以在任何系统上工作,因此是最优雅的实现。

  • 关于Linux和windows文件锁定的讨论可以在这里找到。

编辑:原来可以打开&在Windows中也可以从多个进程中读取临时文件。参见Piotr Dobrogost的回答

with语句中使用mkstemp()代替os.fdopen()可以避免不得不调用close():

fd, path = tempfile.mkstemp()
try:
    with os.fdopen(fd, 'wb') as fileTemp:
        fileTemp.write(someStuff)
    # ...run the subprocess and wait for it to complete...
finally:
    os.remove(path)

我知道这是一个非常旧的帖子,但我认为它是相关的今天,因为API正在改变,像mktemp和mkstemp这样的函数被像TemporaryFile()和TemporaryDirectory()这样的函数所取代。我只是想在下面的示例中演示如何确保临时目录在下游仍然可用:

而不是编码:

tmpdirname = tempfile.TemporaryDirectory()

和在你的代码中使用tmpdirname,你应该尝试在with语句块中使用你的代码,以确保它对你的代码调用是可用的…这样的:

with tempfile.TemporaryDirectory() as tmpdirname:
    [do dependent code nested so it's part of the with statement]

如果你在with之外引用它,那么它很可能不再是可见的

最新更新