PythonのResourceWarning(unclosed file)の原因と対処法|with文とtracemalloc

先に結論です。ResourceWarning: unclosed file ... は「ファイルや接続を閉じ忘れている」という警告です。with 文を使えば解決します。小さなスクリプトでは実害が出にくいため軽視されがちですが、長く動くプログラムでは確実に問題になります。

どんなメッセージが出るか

ResourceWarning: unclosed file <_io.TextIOWrapper name='data.txt' mode='r' encoding='UTF-8'>
ResourceWarning: Enable tracemalloc to get the object allocation traceback

2行目は「どこで開いたか知りたければ tracemalloc を有効にしてください」という案内です。あとで有効にする方法を説明します。

なぜ閉じ忘れが問題なのか

OSが1つのプロセスに許すファイルの同時オープン数には上限があります。閉じないまま開き続けると、いずれ上限に達します。

OSError: [Errno 24] Too many open files
プログラムの種類 閉じ忘れの影響
1回動いて終わるスクリプト ほぼ無害(終了時にOSが回収する)
ループで大量のファイルを処理 途中で上限に達して落ちる
Webサーバー・常駐プロセス 時間とともに悪化し、いずれ停止
データベース接続 接続数の上限に達し、他の処理も巻き添え
「うちは大丈夫」と思えるのは最初のケースだけです

CPythonでは、変数が参照されなくなった時点でファイルが閉じられます。これが「閉じなくても動いてしまう」理由です。ただしこれはCPython固有の動作で、言語の仕様ではありません。PyPyなど他の実装では、いつ閉じられるか保証されません。

直し方|with文を使う

# 悪い例:閉じ忘れる
f = open("data.txt")
data = f.read()
# ここで例外が出たら、close() に到達しない
f.close()

# 良い例:with を使う
with open("data.txt") as f:
    data = f.read()
# ブロックを抜けた時点で必ず閉じられる(例外が出ても閉じる)

with の利点は「例外が出ても必ず閉じる」ことです。try / finally を書かなくても同じ効果が得られます。

withが使えるもの

対象 書き方
ファイル with open(path) as f:
SQLite with sqlite3.connect(path) as conn:(※注意あり)
ソケット with socket.create_connection(addr) as s:
requests with requests.Session() as s:
複数同時 with open(a) as f1, open(b) as f2:
ほとんどの入出力オブジェクトが対応しています

sqlite3の with は接続を閉じません。トランザクションを確定するだけで、接続は開いたままです。紛らわしいので、明示的に conn.close() を呼ぶか、contextlib.closing() で包んでください。

from contextlib import closing
import sqlite3

with closing(sqlite3.connect("db.sqlite")) as conn:
    with conn:                 # ここがトランザクション
        conn.execute("INSERT INTO t VALUES (1)")
# ここで接続が閉じられる

どこで開いたか突き止める

警告だけではどの行で開いたファイルか分かりません。2つの方法で特定できます。

1. tracemalloc を有効にする

python -X tracemalloc=5 your_script.py

開いた場所のトレースバックが警告と一緒に表示されます。数字は遡る段数です。

2. 警告をエラーにする

python -W error::ResourceWarning your_script.py

警告が例外になり、閉じ忘れが検出された時点で処理が止まります。ただし止まるのは「閉じ忘れが判明したとき」で、開いた行ではありません。場所の特定には tracemalloc のほうが有効です。

なぜ普段は表示されないのか

ResourceWarningは既定で表示されません。利用者に見せる必要のない情報だと判断されているためです。

# 表示する
python -W always::ResourceWarning your_script.py

# コードの中から有効にする
import warnings
warnings.simplefilter("always", ResourceWarning)

開発中やテストでは有効にしておくことを勧めます。本番で Too many open files が出てから探すより、はるかに楽です。

よくある質問

無視しても動いていますが、直すべきですか?

直すべきです。1回で終わるスクリプトなら実害はありませんが、そのコードがループの中や常駐プロセスに移ったときに問題になります。

with文を使っているのに警告が出ます

別の場所で開いています。ライブラリの内部や、open() を直接呼んでいる箇所を探してください。-X tracemalloc=5 で場所が分かります。

Too many open files が出ました

閉じ忘れが上限に達しています。上限を上げる前に、まず閉じ忘れを直してください。上限を上げても、漏れ続ける限りいずれ同じことが起きます。

requestsでも出ますか?

出ます。requests.Session() を使い捨てにしていると警告されます。with requests.Session() as s: で包んでください。

関連記事

実行時の警告はRuntimeWarning、ファイルが見つからない場合はFileNotFoundErrorをご覧ください。

Pythonのエラー全般はPythonでよく出るエラー一覧、例外処理の書き方はPythonの例外処理(try except)、警告の扱い方はPythonのWarningにまとめています。

テックジムは月額22,000円の通い放題でプログラミング教室を運営しています。東京本校(永田町)では毎週土曜にゼロから始めるPython入門講座、そしてゼロから始めるClaude Code講座を無料で開いています。どちらも説明会ではなく実際に手を動かす講座です。

まとめ

ResourceWarning: unclosed file は閉じ忘れの警告です。with 文を使えば、例外が出ても必ず閉じられます。

1回で終わるスクリプトなら実害はありませんが、ループや常駐プロセスでは Too many open files で落ちます。場所を特定したいときは python -X tracemalloc=5 で実行してください。sqlite3の with は接続を閉じない点にも注意です。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら