【Python】作業用ファイルを残さない:TemporaryDirectoryで一時領域を管理する

PythonのTopに戻る

中間ファイルの寿命をwithで区切る

画像変換や集計の途中で作った作業ファイルが毎回残るなら、TemporaryDirectoryを使い、処理の期間だけ存在する専用フォルダーへまとめるとよい。withブロックを抜けると、その中のファイルとフォルダーが片付けられる。正常終了だけでなく、通常の例外でブロックを抜けたときも後始末の対象になる。

重要なのは、残す成果物と消してよい中間物を分けることである。一時フォルダーの中に最終結果を作ったままパスだけ返しても、ブロック終了後にはそのパスは使えない。必要な結果はメモリ上の値として返すか、withを抜ける前に別の保存先へ書き出す。以下では実データに触れないよう、最終結果の保存先も外側の一時領域へ作る。

正常終了と例外時の両方を確認する

example.py

from pathlib import Path
from tempfile import TemporaryDirectory


with TemporaryDirectory() as sandbox:
    output = Path(sandbox) / "result.txt"
    with TemporaryDirectory(prefix="convert-", dir=sandbox) as work:
        work_path = Path(work)
        intermediate = work_path / "intermediate.txt"
        intermediate.write_text("alpha\nbeta\n", encoding="utf-8")
        with intermediate.open(encoding="utf-8") as stream:
            result = stream.read().upper()
        output.write_text(result, encoding="utf-8")
        assert intermediate.exists()
    assert not work_path.exists()
    assert output.read_text(encoding="utf-8") == "ALPHA\nBETA\n"
    print("normal cleanup:", not work_path.exists())
    print("result:", output.read_text(encoding="utf-8").splitlines())

    try:
        with TemporaryDirectory(prefix="failed-", dir=sandbox) as work:
            failed_path = Path(work)
            (failed_path / "partial.txt").write_text("partial", encoding="utf-8")
            raise RuntimeError("simulated conversion failure")
    except RuntimeError:
        assert not failed_path.exists()
        print("exception cleanup:", not failed_path.exists())

実行結果

normal cleanup: True
result: ['ALPHA', 'BETA']
exception cleanup: True

ファイルを先に閉じる理由

内側のファイルを開く処理にもwithを使っている。ファイルのwithが先に終わり、その後でディレクトリのwithが終わるため、開いたままのファイルを削除しようとする状況を避けやすい。Windowsなどでは開いているファイルの削除が拒否される場合がある。Linuxで削除できたから、すべてのOSで同じように動くとは限らない。

TemporaryDirectoryが返すのはフォルダー名の文字列なので、Path(work)へ変換すると中のパスを組み立てやすい。prefixは人が調べたときに用途を分かりやすくするための接頭辞である。実際の名前は自動生成されるため、固定名の作業フォルダーを複数の処理で取り合うことを減らせる。

dirを指定しない場合はシステム側の一時領域が使われる。大きな中間ファイルを扱うなら、その保存先に十分な空き容量があるか、メモリ上のファイルシステムではないかなどを確認する。例ではsandboxを指定し、作成するすべてのファイルを管理された一つの領域へ収めている。

片付けられる場合と、保証できない場合

例のRuntimeErrorは、後始末を確認するために意図的に発生させた例外である。外側のexceptで捕まえる時点では一時フォルダーが消えていることをassertしている。ただし、プロセスの強制終了、OSの停止、停電ではPythonの後始末が実行されない場合がある。TemporaryDirectoryはどんな終了方法でも必ず痕跡が残らない機能ではない。

削除する権限が変わったり、別のプロセスがファイルを使用していたりすれば、後始末そのものが失敗することもある。失敗を無条件に無視すると、一時ファイルが残っていることに気づきにくくなる。例では既定の動作を使い、後始末の失敗を隠す設定は加えていない。実運用では残った場所が分かる記録を用意すると調査しやすい。

一時ディレクトリは暗号化された保管庫ではない。消去は通常のファイル削除であり、媒体上のデータの完全な消去を保証するものでもない。機密データを一時的に保存する場合は、権限、保存先、バックアップや同期の対象かを確認する。必要のない個人情報を中間ファイルへ出さない設計も重要である。

例外を残しながらデバッグする

失敗時の中間データを調べたい場合でも、一時フォルダーを永久に残す変更をそのまま通常運用へ持ち込まない。必要なログや小さな診断結果だけを、明示した保存先へコピーしてから例外を伝える方法がある。残す情報と消す情報の境界を決めれば、片付けと調査を両立しやすい。

返り値に一時パスを含める設計では、そのパスを使える期間を呼び出し側へ伝える必要がある。関数の外で使うファイルなら、呼び出し側が一時領域のwithを管理する形へ変えると寿命が明確になる。資源を作る場所と、その資源を使い終える場所を同じ階層で管理するのが基本になる。

実行環境と関連情報

掲載コードはLinux上のCPython 3.12.14で実行した。OS固有のファイル操作や対話環境の違いは、本文に記した条件に従って扱う。

関連:保存途中で元ファイルを壊さない:一時ファイルから置き換える / fixtureとtmp_pathで実データを壊さずファイル処理をテストする

PythonのTopに戻る