简体   繁体   English

如何创建一个子进程可以读取的临时文件?

[英]How to create a temporary file that can be read by a subprocess?

I'm writing a Python script that needs to write some data to a temporary file, then create a subprocess running a C++ program that will read the temporary file.我正在编写一个 Python 脚本,该脚本需要将一些数据写入临时文件,然后创建一个运行 C++ 程序的子进程,该程序将读取临时文件。 I'm trying to use NamedTemporaryFile for this, but according to the docs,我正在尝试为此使用NamedTemporaryFile ,但根据文档,

Whether the name can be used to open the file a second time, while the named temporary file is still open, varies across platforms (it can be so used on Unix; it cannot on Windows NT or later).在命名的临时文件仍处于打开状态时,该名称是否可用于第二次打开文件,因平台而异(在 Unix 上可以这样使用;在 Windows NT 或更高版本上不能)。

And indeed, on Windows if I flush the temporary file after writing, but don't close it until I want it to go away, the subprocess isn't able to open it for reading.事实上,在 Windows 上,如果我在写入后刷新临时文件,但在我希望它消失之前不要关闭它,子进程将无法打开它进行读取。

I'm working around this by creating the file with delete=False , closing it before spawning the subprocess, and then manually deleting it once I'm done:我正在通过使用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)

This seems inelegant.这似乎不优雅。 Is there a better way to do this?有一个更好的方法吗? Perhaps a way to open up the permissions on the temporary file so the subprocess can get at it?也许是一种打开临时文件权限的方法,以便子进程可以访问它?

Since nobody else appears to be interested in leaving this information out in the open...由于似乎没有其他人有兴趣公开这些信息......

tempfile does expose a function, mkdtemp() , which can trivialize this problem: 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)

I leave the implementation of the intermediate functions up to the reader, as one might wish to do things like use mkstemp() to tighten up the security of the temporary file itself, or overwrite the file in-place before removing it.我将中间函数的实现留给读者,因为人们可能希望使用mkstemp()来加强临时文件本身的安全性,或者在删除文件之前就地覆盖文件。 I don't particularly know what security restrictions one might have that are not easily planned for by perusing the source of tempfile .我不特别知道通过仔细tempfile的源代码可能不容易计划的安全限制。

Anyway, yes, using NamedTemporaryFile on Windows might be inelegant, and my solution here might also be inelegant, but you've already decided that Windows support is more important than elegant code, so you might as well go ahead and do something readable.无论如何,是的,在 Windows 上使用NamedTemporaryFile可能不优雅,我在这里的解决方案也可能不优雅,但是您已经确定 Windows 支持比优雅的代码更重要,所以您不妨继续做一些可读的事情。

According to Richard Oudkerk根据理查德 Oudkerk

(...) the only reason that trying to reopen a NamedTemporaryFile fails on Windows is because when we reopen we need to use O_TEMPORARY . (...) 在 Windows 上尝试重新打开NamedTemporaryFile失败的唯一原因是,当我们重新打开时,我们需要使用O_TEMPORARY

and he gives an example of how to do this in Python 3.3+他给出了一个如何在 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)

Because there's no opener parameter in the built-in open() in Python 2.x, we have to combine lower level os.open() and os.fdopen() functions to achieve the same effect:因为 Python 2.x 中内置的open()中没有opener参数,所以我们必须结合较低级别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

You can always go low-level, though am not sure if it's clean enough for you:你总是可以去低级,但不确定它是否对你来说足够干净:

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)

At least if you open a temporary file using existing Python libraries, accessing it from multiple processes is not possible in case of Windows.至少如果您使用现有的 Python 库打开一个临时文件,则在 Windows 的情况下无法从多个进程访问它。 According to MSDN you can specify a 3rd parameter ( dwSharedMode ) shared mode flag FILE_SHARE_READ to CreateFile() function which:根据MSDN ,您可以为CreateFile()函数指定第三个参数( dwSharedMode )共享模式标志FILE_SHARE_READ

Enables subsequent open operations on a file or device to request read access.启用对文件或设备的后续打开操作以请求读取访问权限。 Otherwise, other processes cannot open the file or device if they request read access.否则,如果其他进程请求读取访问权限,它们将无法打开文件或设备。 If this flag is not specified, but the file or device has been opened for read access, the function fails.如果未指定此标志,但已打开文件或设备以进行读取访问,则函数失败。

So, you can write a Windows specific C routine to create a custom temporary file opener function, call it from Python and then you can make your sub-process access the file without any error.因此,您可以编写一个 Windows 特定的 C 例程来创建一个自定义的临时文件打开器函数,从 Python 调用它,然后您可以让您的子进程访问该文件而不会出现任何错误。 But I think you should stick with your existing approach as it is the most portable version and will work on any system and thus is the most elegant implementation.但我认为你应该坚持你现有的方法,因为它是最便携的版本,可以在任何系统上工作,因此是最优雅的实现。

  • Discussion on Linux and windows file locking can be found here .关于 Linux 和 windows 文件锁定的讨论可以在这里找到。

EDIT: Turns out it is possible to open & read the temporary file from multiple processes in Windows too.编辑:原来也可以从 Windows 中的多个进程打开和读取临时文件。 See Piotr Dobrogost's answer .请参阅 Piotr Dobrogost 的回答

Using mkstemp() instead with os.fdopen() in a with statement avoids having to call close() :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)

I know this is a really old post, but I think it's relevant today given that the API is changing and functions like mktemp and mkstemp are being replaced by functions like TemporaryFile() and TemporaryDirectory().我知道这是一篇很老的帖子,但我认为它在今天是相关的,因为 API 正在发生变化,并且 mktemp 和 mkstemp 等函数正在被 TemporaryFile() 和 TemporaryDirectory() 等函数取代。 I just wanted to demonstrate in the following sample how to make sure that a temp directory is still available downstream:我只是想在以下示例中演示如何确保临时目录在下游仍然可用:

Instead of coding:而不是编码:

tmpdirname = tempfile.TemporaryDirectory()

and using tmpdirname throughout your code, you should trying to use your code in a with statement block to insure that it is available for your code calls... like this:并在整个代码中使用 tmpdirname ,您应该尝试在 with 语句块中使用您的代码,以确保它可用于您的代码调用......像这样:

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

If you reference it outside of the with then it's likely that it won't be visible anymore.如果您在 with 之外引用它,那么它很可能不再可见。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM