web-dev-qa-db-ja.com

サブプロセスで読み取れる一時ファイルを作成する方法は?

Python一時ファイルにデータを書き込む必要があるスクリプトを作成し、一時ファイルを読み取るC++プログラムを実行するサブプロセスを作成します。使用しようとしています- 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)

これはエレガントではないようです。これを行うためのより良い方法はありますか?おそらく、サブプロセスがそれを取得できるように一時ファイルのアクセス許可を開く方法ですか?

45
Nathan Reed

少なくとも、既存のPythonライブラリを使用して一時ファイルを開く場合、Windowsの場合、複数のプロセスからのアクセスは不可能です。 [〜#〜] msdn [〜# 〜] 3番目のパラメーター(dwSharedMode)共有モードフラグ_FILE_SHARE_READ_をCreateFile()関数に指定できます。

ファイルまたはデバイスでの後続のオープン操作を有効にして、読み取りアクセスを要求します。そうしないと、他のプロセスが読み取りアクセスを要求した場合、ファイルまたはデバイスを開くことができません。このフラグが指定されていないが、ファイルまたはデバイスが読み取りアクセス用に開かれている場合、関数は失敗します。

したがって、カスタムの一時ファイルオープナー関数を作成するWindows固有のCルーチンを記述し、Pythonから呼び出すと、サブプロセスがエラーなしでファイルにアクセスできるようになります。最もポータブルなバージョンであり、どのシステムでも動作するため、最もエレガントな実装であるため、既存のアプローチに固執する必要があると思います。

  • LinuxおよびWindowsのファイルロックに関する説明は、 こちら にあります。

編集:Windowsの複数のプロセスから一時ファイルを開いて読むことも可能です。 Piotr Dobrogostの answer を参照してください。

9
Nilanjan Basu

誰もこの情報を公開することに興味がないようです...

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のサポートが重要であると既に決めているので、先に進んで読みやすい何かをします。

24
Corbin

According Richard Oudkerkに

(...)WindowsでNamedTemporaryFileを再オープンしようとすると失敗する唯一の理由は、再オープンするときに_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() in 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
_
22
Piotr Dobrogost

あなたはいつでも低レベルに行くことができますが、あなたにとって十分にきれいかどうかはわかりません:

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)
12
Tshepang