tempfile – 一時的なファイルシステムリソースを作成する

目的:一時的なファイルシステムリソースを作成する
利用できるバージョン:1.4 以上、2.3 で主要なセキュリティ修正が施された

多くのプログラムで中間データを書き出すためにファイルを作成する必要性に迫られます。アプリケーションを壊したい攻撃者に推測されないように、セキュアでユニークな名前のファイルを作成することはプログラマの手腕を問われるところです。 tempfile モジュールはセキュアなファイルシステムリソースを作成するための機能を提供します。 TemporaryFile() は無名ファイルをオープンして返します。 NamedTemporaryFile() は名前のあるファイルをオープンして返します。 mkdtemp() は一時的なディレクトリを作成してその名前を返します。

TemporaryFile

あるアプリケーションがデータを保存するために一時的なファイルが必要な場合でも、その一時ファイルを他のプログラムと共有する必要はありません。一時ファイルを作成するために最適な選択は TemporaryFile() 関数です。その関数はプラットホーム上にファイルを作成して即座にアンリンクします。ファイルシステムテーブルにそのファイルへの参照先がないため、他のプログラムがそのファイルをオープンしたり見つけたりすることは不可能です。 TemporaryFile() が作成したファイルは、そのファイルを閉じるときに自動的に削除されます。

import os
import tempfile

print 'Building a file name yourself:'
filename = '/tmp/guess_my_name.%s.txt' % os.getpid()
temp = open(filename, 'w+b')
try:
    print 'temp:', temp
    print 'temp.name:', temp.name
finally:
    temp.close()
    # 自分で一時ファイルを削除する
    os.remove(filename)

print
print 'TemporaryFile:'
temp = tempfile.TemporaryFile()
try:
    print 'temp:', temp
    print 'temp.name:', temp.name
finally:
    # 自動的に一時ファイルを削除する
    temp.close()

この例は名前生成に一般的パターンを使用するのと TemporaryFile() 関数を使用するのを比較して、一時ファイルを作成することの違いを表しています。 TemporaryFile() が返すファイルには名前がないことに注目してください。

$ python tempfile_TemporaryFile.py
Building a file name yourself:
temp: <open file '/tmp/guess_my_name.72105.txt', mode 'w+b' at 0x100458270>
temp.name: /tmp/guess_my_name.72105.txt

TemporaryFile:
temp: <open file '<fdopen>', mode 'w+b' at 0x100458780>
temp.name: <fdopen>

デフォルトで一時ファイルは 'w+b' モードで作成されます。そのため、全てのプラットホーム上で一貫して動作して、その一時ファイルに読み書きすることができます。

import os
import tempfile

temp = tempfile.TemporaryFile()
try:
    temp.write('Some data')
    temp.seek(0)
    
    print temp.read()
finally:
    temp.close()

書き込み後、その一時ファイルからデータを読み込むために seek() を使用してファイル位置を巻き戻す必要があります。

$ python tempfile_TemporaryFile_binary.py
Some data

もしその一時ファイルをテキストモードで動作させたいなら、作成するときに 'w+t' モードをセットしてください。

import tempfile

f = tempfile.TemporaryFile(mode='w+t')
try:
    f.writelines(['first\n', 'second\n'])
    f.seek(0)

    for line in f:
        print line.rstrip()
finally:
    f.close()

その一時ファイルはテキストとしてデータを取り扱います。

$ python tempfile_TemporaryFile_text.py
first
second

NamedTemporaryFile

しかし、名前のある一時的なファイルが重要になる状況もあります。アプリケーションが複数のプロセスやホストにまで及ぶなら、名前のある一時ファイルはアプリケーション間でそのデータをやり取りする最も簡単な方法です。 NamedTemporaryFile() 関数は、名前属性からアクセスされる名前のある一時ファイルを作成します。

import os
import tempfile

temp = tempfile.NamedTemporaryFile()
try:
    print 'temp:', temp
    print 'temp.name:', temp.name
finally:
    # 自動的にファイルを削除する
    temp.close()
print 'Exists after close:', os.path.exists(temp.name)

その一時ファイルに名前が付けられていても、その一時ファイルが閉じられると削除されます。

$ python tempfile_NamedTemporaryFile.py
temp: <open file '<fdopen>', mode 'w+b' at 0x100458270>
temp.name: /var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/tmpcaEzYl
Exists after close: False

mkdtemp

複数の一時ファイルを必要とするなら、1つの一時ディレクトリを作成して、その一時ディレクトリ配下で全ファイルをオープンする方がもっと便利でしょう。一時ディレクトリを作成するために mkdtemp() を使用してください。

import os
import tempfile

directory_name = tempfile.mkdtemp()
print directory_name
# Clean up the directory yourself
os.removedirs(directory_name)

ディレクトリはそれ自身が “オープンされたモノ” ではないので使用後に自分でその一時ディレクトリを削除しなければなりません。

$ python tempfile_mkdtemp.py
/var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/tmp7DqLan

名前を予測する

デバッグ目的のために一時ファイルの名前を予測し易いようにしておくことは役に立ちます。名前のない一時ファイルよりも明らかにセキュアではないですが、名前に予測し易い文字列を含めることはプログラムがその一時ファイルを使用しているときに調査のためにファイルを見つけ易くします。全ての関数は、今のところ、ある程度はファイル名を扱えるように3つの引数を取ります。名前は定型的に生成されます。

dir + prefix + random + suffix

random を除いた全ての値、つまり dir , prefix , suffixTemporaryFile() , NamedTemporaryFile()mkdtemp() への引数として渡されます。例えば、

import tempfile

temp = tempfile.NamedTemporaryFile(suffix='_suffix', 
                                   prefix='prefix_', 
                                   dir='/tmp',
                                   )
try:
    print 'temp:', temp
    print 'temp.name:', temp.name
finally:
    temp.close()

prefixsuffix の引数は、ファイル名生成のためにランダム文字列と組み合わせられます。そして、 dir 引数はそのままで新しいファイルの置き場所として使用されます。

$ python tempfile_NamedTemporaryFile_args.py
temp: <open file '<fdopen>', mode 'w+b' at 0x100458270>
temp.name: /tmp/prefix_og9KcZ_suffix

一時ファイルの場所

dir 引数へ明示的に場所を指定しないなら、その一時ファイルが置かれる実際のパスはプラットホームや設定次第で変わります。 tempfile モジュールには実行時に使用される設定を問い合わせる関数が2つあります。

import tempfile

print 'gettempdir():', tempfile.gettempdir()
print 'gettempprefix():', tempfile.gettempprefix()

gettempdir() は全ての一時ファイルに適用するデフォルトディレクトリを返します。 gettempprefix() は新しいファイルとディレクトリ名のための接頭辞を返します。

$ python tempfile_settings.py
gettempdir(): /var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T
gettempprefix(): tmp

gettempdir() が返す値は現在のプロセスがファイルを作成できるディレクトリリストの最初の場所を調べる線形アルゴリズムに基づいてセットされます。ライブラリドキュメントから引用します。

Python は標準のディレクトリリストを探して、ユーザがファイルを作成できる最初の場所を tempdir にセットします。そのディレクトリリストは次になります。

  1. 一時ディレクトリは TMPDIR 環境変数の名前になります。

  2. 一時ディレクトリは TEMP 環境変数の名前になります。

  3. 一時ディレクトリは TMP 環境変数の名前になります。

  4. プラットホーム固有の場所:

    • RiscOS では一時ディレクトリは Wimp$ScrapDir 環境変数の名前になります。
    • Windows では一時ディレクトリは C:\TEMP , C:\TMP , \TEMP\TMP の順番になります。
    • その他の全プラットホームでは一時ディレクトリは /tmp , /var/tmp/usr/tmp の順番になります。
  5. 最後の手段としてカレントディレクトリになります。

プログラムが全ての一時ファイルにこれらの環境変数をセットせずにグローバルな場所を明示的に必要とするなら、 tempfile.tempdir を直接的にセットすることができます。

import tempfile

tempfile.tempdir = '/I/changed/this/path'
print 'gettempdir():', tempfile.gettempdir()
$ python tempfile_tempdir.py
gettempdir(): /I/changed/this/path

See also

tempfile
本モジュールの標準ライブラリドキュメント
ファイルアクセス
ファイルと共に使用するその他のモジュール
Bookmark and Share