エラーでプログラムを止めずに、ユーザーへ改善や移行を促すにはwarningsモジュールが有効です。
warningsは非推奨通知や注意喚起を行いながら処理を継続できる仕組みで、ライブラリ開発だけでなく自作スクリプトの品質向上にも役立ちます。
本記事では初心者向けに基本から実践的な使い方までを丁寧に解説します。
Pythonのwarningsモジュール入門
エラー(例外)との違い
例外はraise
で送出され、捕捉されない限りプログラムを停止します。
これに対してwarningsはプログラムを停止せずに標準エラー出力へメッセージを表示する通知手段です。
たとえば古い関数の利用や推奨されない引数の組み合わせを発見しても、今すぐ止めずにユーザーへ知らせることができます。
「止めるべき不整合は例外」「知らせて移行させたい事柄は警告」という整理が実務では有効です。
非推奨をユーザーに通知する意味
非推奨(Deprecated)は、現時点では動くが今後削除・変更予定であることを意味します。
計画的な非推奨の告知は、ユーザーの移行期間を確保しつつ破壊的変更による混乱を避けるために不可欠です。
メッセージに移行先や期限を明記すれば、ユーザーは次の一歩を迷わず踏み出せます。
黙って仕様を変えるのは最悪の体験なので、まず警告で予告し、段階的に削除へ向かうのがよい流れです。
よく使うカテゴリ(DeprecationWarning, UserWarning)
warningsには用途ごとにカテゴリ(クラス)があります。
初心者はまず次の2つを押さえれば十分です。
- DeprecationWarning: 非推奨機能の利用通知に使います。将来的に削除・変更予定であることを知らせます。
- UserWarning: ユーザーの入力や設定に注意喚起したい場合に使います(非推奨ではない一般的な警告)。
参考までに、用途の違いを短く整理します。
カテゴリ | 主な用途 | ユーザーへの意図 |
---|---|---|
DeprecationWarning | 機能・引数の非推奨化 | 移行の開始を促す |
UserWarning | 想定外だが実行可能な状況 | 値や設定の見直しを促す |
DeprecationWarningは非推奨の告知専用、UserWarningは一般警告と覚えると判断しやすいです。
warnings.warnの基本的な使い方
DeprecationWarningを出す
最小の例はwarnings.warn
にカテゴリを渡すだけです。
# sample_deprecation.py
import warnings
def old_sum(a, b):
# 非推奨を通知(最小例)
warnings.warn("old_sum(a, b) は非推奨です。new_sum(a, b) を使用してください。", DeprecationWarning)
return a + b
print(old_sum(1, 2))
sample_deprecation.py:6: DeprecationWarning: old_sum(a, b) は非推奨です。new_sum(a, b) を使用してください。
warnings.warn("old_sum(a, b) は非推奨です。new_sum(a, b) を使用してください。", DeprecationWarning)
3
警告は表示されますが処理は継続し、計算結果も出力されることがポイントです。
メッセージの書き方と移行先の案内
良いメッセージは「何が非推奨か」「理由(任意)」「移行先」「スケジュール」を簡潔に含みます。
# sample_message.py
import warnings
REMOVAL_VERSION = "2.0"
def old_api(x):
# 分かりやすい移行案内を含める
msg = (
f"old_api(x) は非推奨です (削除予定: v{REMOVAL_VERSION})。"
"代わりに new_api(x) を使用してください。"
"移行ガイド: https://example.com/migration#old_api"
)
warnings.warn(msg, DeprecationWarning, stacklevel=2) # stacklevel は後述
return new_api(x)
def new_api(x):
return x * 2
# 利用側
print(old_api(10))
sample_message.py:16: DeprecationWarning: old_api(x) は非推奨です (削除予定: v2.0)。代わりに new_api(x) を使用してください。移行ガイド: https://example.com/migration#old_api
print(old_api(10))
20
移行先とドキュメントURLを示すだけで、ユーザーの負担は大きく下がります。
stacklevelで呼び出し元を示す
ライブラリ内で警告を発すると、デフォルトでは「ライブラリ内部の行」が表示されます。
しかしユーザーが直すべきのは自分の呼び出し箇所です。
stacklevelを適切に設定して、ユーザーのコード位置を指し示すのが実務で重要です。
以下は2ファイル構成の例です。
# lib.py
import warnings
def old_api(x):
# ユーザーの呼び出し位置を示したいので stacklevel=2
warnings.warn("old_api(x) は非推奨です。new_api(x) を使ってください。", DeprecationWarning, stacklevel=2)
return new_api(x)
def new_api(x):
return x * 2
# main.py
from lib import old_api
def run():
return old_api(5)
if __name__ == "__main__":
print(run())
main.py:5: DeprecationWarning: old_api(x) は非推奨です。new_api(x) を使ってください。
return old_api(5)
10
stacklevel=2は「warnを発した関数の呼び出し元」を指す設定です。
さらにラッパー関数が重なっている場合は、ユーザーコードに届くまで3
や4
などに増やします。
警告の表示を制御する方法
DeprecationWarningが見えない理由(デフォルト設定)
多くの環境ではDeprecationWarningはデフォルトで非表示(無視)です。
これは、ライブラリ内部の非推奨通知が一般ユーザーに大量に表示され、ノイズになるのを避けるためです。
「警告を出しているのに表示されない」場合は、この既定のフィルタが原因であることを覚えておきましょう。
simplefilterで表示を調整する(default, ignore, always)
コードからグローバルな方針を変えるにはwarnings.simplefilter
を使います。
# sample_simplefilter.py
import warnings
def will_warn(msg):
warnings.warn(f"注意: {msg}", UserWarning)
# 1) 既定相当(default): 同一箇所の警告は最初だけ表示
warnings.simplefilter("default")
print("\n=== filter: default ===")
will_warn("default")
will_warn("default") # 同じ場所でも2回目は出ないことが多い
# 2) 常に表示(always)
warnings.simplefilter("always")
print("\n=== filter: always ===")
will_warn("always")
will_warn("always") # 毎回表示される
# 3) 無視(ignore)
warnings.simplefilter("ignore")
print("\n=== filter: ignore ===")
will_warn("ignore フィルタ") # 表示されない
想定される出力例(環境により行番号等は異なります):
=== filter: default ===
c:\dev\python\sample.py:5: UserWarning: 注意: default
warnings.warn(f"注意: {msg}", UserWarning)
=== filter: always ===
c:\dev\python\sample.py:5: UserWarning: 注意: always
warnings.warn(f"注意: {msg}", UserWarning)
c:\dev\python\sample.py:5: UserWarning: 注意: always
warnings.warn(f"注意: {msg}", UserWarning)
=== filter: ignore ===
よく使うアクションは次のとおりです。
アクション | 挙動の要約 |
---|---|
default | 同一の発生元では最初の1回のみ表示 |
ignore | 表示しない |
always | 毎回表示 |
once | プロセス全体で1回だけ表示 |
module | モジュールごとに1回表示 |
error | 警告を例外に昇格(後述のテストで便利) |
学習や移行期間にはalways、出荷時にはdefault/ignoreを選ぶなど、状況に応じて使い分けます。
filterwarningsで対象を絞る(カテゴリ, モジュール名)
より細かく制御するにはwarnings.filterwarnings
を使います。
メッセージやモジュール名は正規表現で絞り込めます。
# sample_filterwarnings.py
import warnings
def old_feature():
warnings.warn("v2.0で削除予定: old_feature は非推奨", DeprecationWarning)
def risky_but_ok():
warnings.warn("設定AとBの併用は非推奨です", UserWarning)
# まずは全体方針を default に
warnings.simplefilter("default")
# 1) DeprecationWarning のうち、old_feature に関するものは常に表示
warnings.filterwarnings(
action="always",
message=r".*old_feature.*",
category=DeprecationWarning
)
# 2) UserWarning はこのモジュールでは無視
warnings.filterwarnings(
action="ignore",
category=UserWarning,
module=r"^sample_filterwarnings$"
)
old_feature() # 表示される(常に)
risky_but_ok() # 無視される
sample_filterwarnings.py:6: DeprecationWarning: v2.0で削除予定: old_feature は非推奨
warnings.warn("v2.0で削除予定: old_feature は非推奨", DeprecationWarning)
シグネチャはfilterwarnings(action, message="", category=Warning, module="", lineno=0, append=False)
です。
messageやmoduleは正規表現である点に注意してください。
コマンドライン(-W)と環境変数(PYTHONWARNINGS)
コードを変えずに挙動を切り替えたい場合は、起動オプションか環境変数が便利です。
- コマンドラインの例:
# DeprecationWarning を常に表示
python -W always::DeprecationWarning your_script.py
# DeprecationWarning を例外に昇格(テストで有効)
python -W error::DeprecationWarning -m pytest
# 既定の挙動にする(一部環境で有効)
python -W default your_script.py
- 環境変数の例(複数設定はカンマ区切り):
# Linux/macOS
export PYTHONWARNINGS="always::DeprecationWarning,ignore::UserWarning"
python your_script.py
# Windows (PowerShell)
$env:PYTHONWARNINGS="error::DeprecationWarning"
python your_script.py
CIや本番環境で一律に方針を変えたい場合、-W や PYTHONWARNINGS が安全です。
ベストプラクティスと注意点
UserWarningとDeprecationWarningの使い分け
- 移行を促す恒常的な告知にはDeprecationWarningを使います。メッセージに移行先と目安時期を含めましょう。
- 入力値の境界や推奨設定から外れた状況にはUserWarningを使います。たとえば「精度が落ちる可能性がある」などです。
なお、仕様変更の予告としてFutureWarning
を用いるプロジェクトもありますが、本記事では扱いを最小限に留め、まずは上記2種の確実な使い分けを推奨します。
一時的に無視する(catch_warnings)
一時的に方針を変えたり、テストで「警告が出たこと」を検証したいときはwarnings.catch_warnings
が役立ちます。
# sample_catch_warnings.py
import warnings
def old_api():
warnings.warn("old_api は非推奨です", DeprecationWarning)
# 1) 一時的に無視
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
old_api() # ここでは表示されない
# 2) 一時的に記録して検証
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always") # 必ず記録されるように
old_api()
assert len(w) == 1
rec = w[0]
# rec.category, rec.message, rec.filename などで詳細を確認できる
print(rec.category.__name__, str(rec.message))
DeprecationWarning old_api は非推奨です
catch_warningsは局所的に方針を上書きできる安全弁で、ライブラリ内の限定的な抑制やテストでの検証に適しています。
テストで警告をエラーにする(errorに昇格)
古いAPIの残骸や非推奨呼び出しを早期に排除するには、テスト時に警告を例外へ昇格させるのが効果的です。
方法は3通りあります。
# conftest.py や テストの先頭など
import warnings
warnings.simplefilter("error", DeprecationWarning)
pytest -W error::DeprecationWarning
export PYTHONWARNINGS="error::DeprecationWarning"
pytest
テストをレッドにして非推奨呼び出しを可視化することで、移行の先送りを防止できます。
非推奨の段階的な告知と削除の流れ
実務で失敗しにくい進め方を簡潔に示します。
- 導入(新バージョンN): 旧APIに
warnings.warn(..., DeprecationWarning, stacklevel=2)
を追加。ドキュメントとリリースノートに移行先・期限を記載。 - 移行期間(N→N+1): サンプルやガイドを整備。テストでは警告をエラー化し、内部の移行を完了。
- 削除準備(N+1): 旧APIの実装を薄くし、内部で新APIへ委譲。stacklevelでユーザーの呼び出し位置を確実に指す。
- 削除(N+2 以降): 旧APIを削除し、破壊的変更として明示。必要に応じて
CHANGES.md
に移行ガイドを残す。
「予告→並走→削除」の三段階でユーザー体験を損なわずに進めるのがコツです。
まとめ
warningsモジュールは、プログラムを止めずにユーザーへ重要な情報を伝えるための標準装備です。
非推奨の告知にはDeprecationWarning
、一般的な注意喚起にはUserWarning
を使い分け、stacklevelでユーザーの呼び出し箇所を指すことで体験が大きく向上します。
表示制御はsimplefilter
やfilterwarnings
、起動オプション-W
や環境変数PYTHONWARNINGS
を適切に活用してください。
最後に、テストでは警告をエラーへ昇格して早期に不適切な利用を洗い出すことが、健全なコードベースを保つ最短ルートです。