将来の問題を警告の段階で見つける
DeprecationWarningは、今は動くが将来の版で変更・削除される機能を知らせるために使われる。通常の実行で見えない場合があるからといって、警告が存在しないとは限らない。テストでは必要な警告を表示または例外化し、原因を直すまでの間に限って、対象を絞った抑制を使う。
全警告をignoreにすると、別のライブラリや新しく発生した問題まで見えなくなる。以下では自作の小さな旧関数を使い、警告の記録、例外化、特定の警告だけの抑制をそれぞれ確認する。実ライブラリの設定を恒久的に書き換えることはせず、catch_warnings()の範囲内で切り替える。
警告を記録し、必要なら失敗に変える
example.py
import warnings
def old_scale(value):
warnings.warn("old_scale is deprecated", DeprecationWarning, stacklevel=2)
return value * 2
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always", DeprecationWarning)
assert old_scale(3) == 6
assert len(caught) == 1
assert caught[0].category is DeprecationWarning
print(caught[0].category.__name__, str(caught[0].message))
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
try:
old_scale(3)
except DeprecationWarning:
print("promoted to exception")
else:
raise AssertionError("warning did not become an exception")
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
warnings.filterwarnings(
"ignore", message=r"^old_scale is deprecated$",
category=DeprecationWarning,
)
assert old_scale(3) == 6
warnings.warn("check calibration", UserWarning)
assert len(caught) == 1
assert caught[0].category is UserWarning
print("still visible:", str(caught[0].message))
実行結果
DeprecationWarning old_scale is deprecated
promoted to exception
still visible: check calibration
警告の種類と処理方法は別
DeprecationWarningは警告の種類、alwaysやerrorはその警告をどう扱うかの指定である。alwaysにすると同じ警告の繰り返しも記録し、errorにするとその場所で例外として送出する。例外化したold_scale()では、警告の次のreturnへ進む前に止まるため、警告を表示するだけの場合と制御の流れも変わる。
record=Trueを付けると、表示する代わりに警告オブジェクトのリストへ記録できる。件数、category、messageを調べれば、期待する警告が出たかを自動で確認できる。出力の見た目だけを比較するより、警告の種類を直接検査する方がメッセージの表示書式に依存しにくい。
stacklevel=2は、この警告を出した関数そのものではなく、その呼び出し側を場所として示すための指定である。旧APIを使っている場所を見つけやすくなる。何段のラッパーを挟むかによって適切な値は変わるため、常に2を付ければ正しい位置になるわけではない。
抑制する範囲を狭く保つ
filterwarnings()には警告の種類とメッセージのパターンを渡した。^と$で例のメッセージ全体を対象にし、別のUserWarningが残ることも確認している。実際のライブラリでは必要に応じてmoduleなどでさらに絞れるが、stacklevelにより警告の発生位置が呼び出し側になる場合があるので、観測した情報を確認してから条件を決める。
catch_warnings()を抜けると、その範囲で変更したフィルターなどが元へ戻る。テストごとに設定を分離しやすい一方、CPython 3.12の通常の警告状態はプロセス全体に関わる。複数スレッドで同時にフィルターを変更する用途を、単にwithで囲んだから完全に分離されたと考えない。ここでは単一スレッドで実行している。
抑制は原因の修正ではない。何の旧機能を使っていて、どの版で対応する予定かを残し、ライブラリ更新時に不要になった設定を外す。広い条件のignoreが古いまま残ると、その後の別の警告を見落としやすくなる。
通常の実行で見えないとき
DeprecationWarningは、importしたライブラリ内で発生する場合には既定で隠れることがある。一方、__main__として実行するコードに帰属するものは表示対象になるなど、発生場所と既定フィルターによって結果が違う。警告が表示された一例から「常に見える」「常に隠れる」と一般化しない。
端末からはpython -Wdのような起動オプションで警告を見つける方法もある。継続的なテストでは、まず警告を一覧にして既知と新規を分け、対応可能な範囲から例外化すると導入しやすい。依存ライブラリの警告を一括で失敗にする場合は、更新しただけでテストが止まる可能性もあるので、運用の方針を明確にする。
警告を記録するテストでは、その警告が一度出たことだけで満足せず、今後のAPIへ移行した際に警告が出なくなることも確認する。警告の有無と関数の戻り値を両方確かめれば、表示を消すだけの変更で計算結果を壊していないかを調べられる。
実行環境と関連情報
掲載コードはLinux上のCPython 3.12.14で実行した。OS固有のファイル操作や対話環境の違いは、本文に記した条件に従って扱う。
関連:pytestで正常系と異常系の自動テストを始める / 別のPCで環境を再現する:依存関係の記録と入れ直し
- Python公式:warnings(2026年10月2日参照)
