pythonでwarningsパッケージを使った警告の表示のメモ
警告を表示したいことがある。例えば、次のversionで廃止予定の関数を使った時の警告など。 こういうときには warningsパッケージを使う。
first step
00warn.py
import warnings def foo(): warnings.warn("hmm") return "hai" if __name__ == "__main__": print(foo()) print(foo())
warnings.warn
で警告メッセージを表示する。defaultではUserWarningの警告レベルで警告メッセージが出る。同じメッセージは2度表示されない。
$ python 00warn.py 00warn.py:5: UserWarning: hmm warnings.warn("hmm") hai
開発者向けのDeprecationWarning
開発者向けにDeprecationWarningの警告レベルで警告メッセージを表示したい場合がある。これは通常のpythonの実行では表示されない。
01warn.py
import warnings def foo(): warnings.warn("hmm") warnings.warn("hmm. foo is deprecated", DeprecationWarning) return "hai" if __name__ == "__main__": print(foo()) print(foo())
通常のpythonの実行では表示されない。-W default
などを付けて実行すると表示されるようになる
$ python 02warn.py 02warn.py:5: UserWarning: hmm warnings.warn("hmm") hai hai $ python -W default 02warn.py 02warn.py:5: UserWarning: hmm warnings.warn("hmm") 02warn.py:6: DeprecationWarning: hmm. foo is deprecated warnings.warn("hmm. foo is deprecated", DeprecationWarning) hai hai
すごくまじめにAPIの機能を廃止する時の作業
APIの機能を廃止する時の作業はまじめにやるなら以下の様な形になる。
- PendingDeprecationWarning (将来消される予定のAPI)
- DeprecationWarning (既に廃止されているAPI。ただし移行期間用にまだコードは消されていない)
- APIを消す
DeprecationWarningは眼に見えないという点がちょっとつらいかもしれない?
一時的に警告をfilterしたい場合
capture_warningsとsimple_filterを組み合せると良い。引数に渡す値は以下くらいの雑な認識良さそう。
- always 全部表示
- ignore 全部無視
03warn.py
import warnings def foo(): warnings.warn("hmm") warnings.warn("hmm. foo is deprecated", DeprecationWarning) warnings.warn("hmm. foo is deprecated on next version", PendingDeprecationWarning) return "hai" if __name__ == "__main__": print("1.") print("----------------------------------------") with warnings.catch_warnings(): warnings.simplefilter("ignore") print(foo()) print("2.") print("----------------------------------------") with warnings.catch_warnings(): warnings.simplefilter("always") print(foo())
1回目のfoo呼び出しでは全ての警告メッセージが無視される。 2回目のfoo呼び出しでは全ての警告メッセージを表示される。
実行結果。
$ python 03warn.py 1. ---------------------------------------- hai 2. ---------------------------------------- 03warn.py:5: UserWarning: hmm warnings.warn("hmm") 03warn.py:6: DeprecationWarning: hmm. foo is deprecated warnings.warn("hmm. foo is deprecated", DeprecationWarning) 03warn.py:7: PendingDeprecationWarning: hmm. foo is deprecated on next version warnings.warn("hmm. foo is deprecated on next version", PendingDeprecationWarning) hai
警告メッセージを表示するdecoratorを作る場合
警告メッセージを表示するdecoratorを作る場合はwarnings.warnのstacklevelを調整する。
05withdecorator.py
import warnings def useless(msg, cls=UserWarning): def _useless(fn): def decorated(*args, **kwargs): warnings.warn(msg, stacklevel=2) return fn(*args, **kwargs) return decorated return _useless @useless("foo is useless") def foo(): return "foo" if __name__ == "__main__": print(foo())
呼び出した位置をしっかり警告してくれる。
$ python 05withdecorator.py 05withdecorator.py:21: UserWarning: foo is useless print(foo()) foo
警告のテストの書き方
警告のテストについては、ドキュメントにも例が載っている
pyramidというweb application frameworkのテストを例に警告の表示のテストの書き方のメモ。
tests/test_config/test_util.py
class TestDeprecatedPredicates(unittest.TestCase): def test_it(self): import warnings with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always') from pyramid.config.predicates import XHRPredicate self.assertEqual(len(w), 1)
capture_warningsで記録しておく。警告が記録されたら増えるのでlenで長さを確認する。
see also
この記事は分かりやすかった