Как создать временный файл, который может быть прочитан подпроцессом?
Я пишу 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)
Это кажется неэлегантным. Есть лучший способ сделать это? Возможно, способ открыть разрешения для временного файла, чтобы подпроцесс мог получить на нем?
Ответы
Ответ 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)