Подтвердить что ты не робот

Как создать временный файл, который может быть прочитан подпроцессом?

Я пишу Python script, который должен записать некоторые данные во временный файл, а затем создать подпроцесс, на котором запущена программа на С++, которая будет читать временный файл. Я пытаюсь использовать NamedTemporaryFile для этого, но, согласно документам,

Можно ли использовать имя для открытия файла во второй раз, тогда как именованный временный файл все еще открыт, он варьируется в разных платформах (его можно использовать в Unix, он не может работать в Windows NT или более поздней версии).

И действительно, в 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)

Это кажется неэлегантным. Есть лучший способ сделать это? Возможно, способ открыть разрешения для временного файла, чтобы подпроцесс мог получить на нем?

4b9b3361

Ответ 1

По крайней мере, если вы открываете временный файл с использованием существующих библиотек Python, доступ к нему из нескольких процессов невозможен в случае Windows. Согласно MSDN вы можете указать флаг общего режима 3-го параметра (dwSharedMode) FILE_SHARE_READ to CreateFile(), который:

Включает последующие операции открытого доступа к файлу или устройству для запроса чтения доступ. В противном случае другие процессы не смогут открыть файл или устройство, если они запрашивают доступ на чтение. Если этот флаг не указан, но файл или устройство открыто для доступа к чтению, функция не работает.

Итак, вы можете написать подпрограмму C для Windows, чтобы создать пользовательскую функцию временного открытия файлов, вызвать ее из Python, а затем вы можете сделать ваш подпроцесс доступ к файлу без каких-либо ошибок. Но я думаю, что вы должны придерживаться своего существующего подхода, поскольку он является самой портативной версией и будет работать в любой системе и, таким образом, является самой элегантной реализацией.

  • Обсуждение Linux и блокировки файлов Windows можно найти здесь.

EDIT: Оказывается, можно открыть и прочитать временный файл из нескольких процессов в Windows. См. Piotr Dobrogost ответ.

Ответ 2

Поскольку никто другой, похоже, не заинтересован в том, чтобы оставить эту информацию открытой...

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)

Я оставляю реализацию промежуточных функций до читателя, так как можно было бы использовать такие вещи, как use mkstemp(), чтобы ужесточить безопасность самого временного файла или перезаписать файл на месте перед его удалением. Я не особо знаю, какие ограничения безопасности могут иметь, которые нелегко спланировать, просмотрев источник tempfile.

Во всяком случае, да, использование NamedTemporaryFile в Windows может быть неэлегантным, и мое решение здесь также может быть неэлегантным, но вы уже решили, что поддержка Windows важнее элегантного кода, поэтому вы также можете пойти вперед и сделать что-то читаемым.

Ответ 3

Согласно Ричарду Оудкерку

(...) единственная причина, по которой попытка повторного открытия 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)

Поскольку во встроенном open() в Python 2.x нет параметра 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

Ответ 4

Вы всегда можете перейти на низкоуровневый уровень, хотя не уверены, достаточно ли он достаточно для вас:

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)