warnings --- 警告の制御

ソースコード: Lib/warnings.py


警告メッセージは一般に、ユーザに警告しておいた方がよいような状況下にプログラムが置かれているが、その状況は (通常は) 例外を送出したりそのプログラムを終了させるほどの正当な理由がないといった状況で発されます。例えば、プログラムが古いモジュールを使っている場合には警告を発したくなるかもしれません。

Python プログラマは、このモジュールの warn() 関数を使って警告を発することができます。(C 言語のプログラマは PyErr_WarnEx() を使います; 詳細は 例外処理 を参照してください)。

警告メッセージは通常 sys.stderr に出力されますが、その処理方法は柔軟に変更することができます。すべての警告を無視することも、警告を例外に変更することもできます。警告の処理方法は警告カテゴリ (以下参照)、警告メッセージテキスト、そして警告を発したソースコード上の場所に基づいて変更することができます。ソースコード上の同じ場所に対して特定の警告が繰り返された場合、通常は抑制されます。

警告制御には 2 つの段階 (stage) があります: 第一に、警告が発されるたびに、メッセージを出力すべきかどうかの決定が行われます; 次に、メッセージを出力するなら、メッセージはユーザによって設定が可能なフックを使って書式化され印字されます。

警告メッセージを出力するかどうかの決定は、警告フィルタによって制御されます。警告フィルタは一致規則 (matching rule)と動作からなるシーケンスです。 filterwarnings() を呼び出して一致規則をフィルタに追加することができ、 resetwarnings() を呼び出してフィルタを標準設定の状態にリセットすることができます。

警告メッセージの印字は showwarning() を呼び出して行うことができ、この関数は上書きすることができます; この関数の標準の実装では、 formatwarning() を呼び出して警告メッセージを書式化しますが、この関数についても自作の実装を使うことができます。

参考

logging.captureWarnings() を使うことで、標準ロギング基盤ですべての警告を扱うことができます。

警告カテゴリ

There are a number of built-in exceptions that represent warning categories. This categorization is useful to be able to filter out groups of warnings.

While these are technically built-in exceptions, they are documented here, because conceptually they belong to the warnings mechanism.

標準の警告カテゴリをユーザの作成したコード上でサブクラス化することで、さらに別の警告カテゴリを定義することができます。警告カテゴリは常に Warning クラスのサブクラスでなければなりません。

The following warnings category classes are currently defined:

Class 説明
Warning すべての警告カテゴリクラスの基底クラスです。 Exception のサブクラスです。
UserWarning warn() の標準のカテゴリです。
DeprecationWarning Base category for warnings about deprecated features when those warnings are intended for other Python developers (ignored by default, unless triggered by code in __main__).
SyntaxWarning その文法機能があいまいであることを示す警告カテゴリの基底クラスです。
RuntimeWarning そのランタイム機能があいまいであることを示す警告カテゴリの基底クラスです。
FutureWarning Base category for warnings about deprecated features when those warnings are intended for end users of applications that are written in Python.
PendingDeprecationWarning 将来その機能が廃止されることを示す警告カテゴリの基底クラスです(デフォルトでは無視されます)。
ImportWarning モジュールのインポート処理中に引き起こされる警告カテゴリの基底クラスです(デフォルトでは無視されます)。
UnicodeWarning Unicode に関係した警告カテゴリの基底クラスです。
BytesWarning bytesbytearray に関連した警告カテゴリの基底クラスです。
ResourceWarning リソースの使用に関連した警告カテゴリの基底クラスです。

バージョン 3.7 で変更: Previously DeprecationWarning and FutureWarning were distinguished based on whether a feature was being removed entirely or changing its behaviour. They are now distinguished based on their intended audience and the way they're handled by the default warnings filters.

警告フィルタ

警告フィルタは、ある警告を無視すべきか、表示すべきか、あるいは (例外を送出する) エラーにするべきかを制御します。

Conceptually, the warnings filter maintains an ordered list of filter specifications; any specific warning is matched against each filter specification in the list in turn until a match is found; the filter determines the disposition of the match. Each entry is a tuple of the form (action, message, category, module, lineno), where:

  • action は以下の文字列のうちの一つです:

    処理方法
    "default" print the first occurrence of matching warnings for each location (module + line number) where the warning is issued
    "error" 一致した警告を例外に変えます
    "ignore" 一致した警告を出力しません
    "always" 一致した警告を常に出力します
    "module" print the first occurrence of matching warnings for each module where the warning is issued (regardless of line number)
    "once" 一致した警告のうち、警告の原因になった場所にかかわらず最初の警告のみ出力します
  • message は正規表現を含む文字列で、警告メッセージの先頭はこのパターンに一致しなければなりません。正規表現は常に大小文字の区別をしないようにコンパイルされます。

  • category はクラス (Warning のサブクラス) です。警告クラスはこのクラスのサブクラスに一致しなければなりません。

  • module は正規表現を含む文字列で、モジュール名はこのパターンに一致しなければなりません。正規表現は常に大小文字の区別をしないようにコンパイルされます。

  • lineno は整数で、警告が発生した場所の行番号に一致しなければなりません。0 の場合はすべての行と一致します。

Warning クラスは組み込みの Exception クラスから派生しているので、警告をエラーに変換するには単に category(message)raise します。

If a warning is reported and doesn't match any registered filter then the "default" action is applied (hence its name).

Describing Warning Filters

The warnings filter is initialized by -W options passed to the Python interpreter command line and the PYTHONWARNINGS environment variable. The interpreter saves the arguments for all supplied entries without interpretation in sys.warnoptions; the warnings module parses these when it is first imported (invalid options are ignored, after printing a message to sys.stderr).

Individual warnings filters are specified as a sequence of fields separated by colons:

action:message:category:module:line

The meaning of each of these fields is as described in 警告フィルタ. When listing multiple filters on a single line (as for PYTHONWARNINGS), the individual filters are separated by commas,and the filters listed later take precedence over those listed before them (as they're applied left-to-right, and the most recently applied filters take precedence over earlier ones).

Commonly used warning filters apply to either all warnings, warnings in a particular category, or warnings raised by particular modules or packages. Some examples:

default                      # Show all warnings (even those ignored by default)
ignore                       # Ignore all warnings
error                        # Convert all warnings to errors
error::ResourceWarning       # Treat ResourceWarning messages as errors
default::DeprecationWarning  # Show DeprecationWarning messages
ignore,default:::mymodule    # Only report warnings triggered by "mymodule"
error:::mymodule[.*]         # Convert warnings to errors in "mymodule"
                             # and any subpackages of "mymodule"

Default Warning Filter

By default, Python installs several warning filters, which can be overridden by the -W command-line option, the PYTHONWARNINGS environment variable and calls to filterwarnings().

In regular release builds, the default warning filter has the following entries (in order of precedence):

default::DeprecationWarning:__main__
ignore::DeprecationWarning
ignore::PendingDeprecationWarning
ignore::ImportWarning
ignore::ResourceWarning

In debug builds, the list of default warning filters is empty.

バージョン 3.2 で変更: PendingDeprecationWarning に加えて、 DeprecationWarning もデフォルトで無視されるようになりました。

バージョン 3.7 で変更: DeprecationWarning is once again shown by default when triggered directly by code in __main__.

バージョン 3.7 で変更: BytesWarning no longer appears in the default filter list and is instead configured via sys.warnoptions when -b is specified twice.

Overriding the default filter

Developers of applications written in Python may wish to hide all Python level warnings from their users by default, and only display them when running tests or otherwise working on the application. The sys.warnoptions attribute used to pass filter configurations to the interpreter can be used as a marker to indicate whether or not warnings should be disabled:

import sys

if not sys.warnoptions:
    import warnings
    warnings.simplefilter("ignore")

Developers of test runners for Python code are advised to instead ensure that all warnings are displayed by default for the code under test, using code like:

import sys

if not sys.warnoptions:
    import os, warnings
    warnings.simplefilter("default") # Change the filter in this process
    os.environ["PYTHONWARNINGS"] = "default" # Also affect subprocesses

Finally, developers of interactive shells that run user code in a namespace other than __main__ are advised to ensure that DeprecationWarning messages are made visible by default, using code like the following (where user_ns is the module used to execute code entered interactively):

import warnings
warnings.filterwarnings("default", category=DeprecationWarning,
                                   module=user_ns.get("__name__"))

一時的に警告を抑制する

If you are using code that you know will raise a warning, such as a deprecated function, but do not want to see the warning (even when warnings have been explicitly configured via the command line), then it is possible to suppress the warning using the catch_warnings context manager:

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    fxn()

このサンプルのコンテキストマネージャーの中では、すべての警告が無視されています。これで、他の廃止予定のコードを含まない(つもりの)部分まで警告を抑止せずに、廃止予定だと分かっているコードだけ警告を表示させないようにすることができます。注意: これが保証できるのはシングルスレッドのアプリケーションだけです。 2つ以上のスレッドが同時に catch_warnings コンテキストマネージャーを使用した場合、動作は未定義です。

警告のテスト

コードが警告を発生させることをテストするには、 catch_warnings コンテキストマネージャーを利用します。このクラスを使うと、一時的に警告フィルタを操作してテストに利用できます。例えば、次のコードでは、発生したすべての警告を取得してチェックしています:

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")
    # Trigger a warning.
    fxn()
    # Verify some things
    assert len(w) == 1
    assert issubclass(w[-1].category, DeprecationWarning)
    assert "deprecated" in str(w[-1].message)

always の代わりに error を利用することで、すべての警告で例外を発生させることができます。1つ気をつけないといけないのは、一度 once/default ルールによって発生した警告は、フィルタに何をセットしているかにかかわらず、警告レジストリをクリアしない限りは 2度と発生しません。

コンテキストマネージャーが終了したら、警告フィルタはコンテキストマネージャーに入る前のものに戻されます。これは、テスト中に予期しない方法で警告フィルタが変更され、テスト結果が中途半端になる事を予防します。このモジュールの showwarning() 関数も元の値に戻されます。注意: これが保証できるのはシングルスレッドのアプリケーションだけです。 2つ以上のスレッドが同時に catch_warnings コンテキストマネージャを使用した場合、動作は未定義です。

同じ種類の警告を発生させる複数の操作をテストする場合、各操作が新しい警告を発生させている事を確認するのは大切な事です (例えば、警告を例外として発生させて各操作が例外を発生させることを確認したり、警告リストの長さが各操作で増加していることを確認したり、警告リストを各操作の前に毎回クリアする事ができます)。

Updating Code For New Versions of Dependencies

Warning categories that are primarily of interest to Python developers (rather than end users of applications written in Python) are ignored by default.

Notably, this "ignored by default" list includes DeprecationWarning (for every module except __main__), which means developers should make sure to test their code with typically ignored warnings made visible in order to receive timely notifications of future breaking API changes (whether in the standard library or third party packages).

In the ideal case, the code will have a suitable test suite, and the test runner will take care of implicitly enabling all warnings when running tests (the test runner provided by the unittest module does this).

In less ideal cases, applications can be checked for use of deprecated interfaces by passing -Wd to the Python interpreter (this is shorthand for -W default) or setting PYTHONWARNINGS=default in the environment. This enables default handling for all warnings, including those that are ignored by default. To change what action is taken for encountered warnings you can change what argument is passed to -W (e.g. -W error). See the -W flag for more details on what is possible.

利用可能な関数

warnings.warn(message, category=None, stacklevel=1, source=None)

警告を発するか、無視するか、あるいは例外を送出します。 category 引数が与えられた場合、警告カテゴリクラスでなければなりません (上記を参照してください); 標準の値は UserWarning です。 messageWarning インスタンスで代用することもできますが、この場合 category は無視され、 message.__class__ が使われ、メッセージ文は str(message) になります。発された例外が前述した警告フィルタによってエラーに変更された場合、この関数は例外を送出します。引数 stacklevel は Python でラッパー関数を書く際に利用することができます。例えば:

def deprecation(message):
    warnings.warn(message, DeprecationWarning, stacklevel=2)

こうすることで、警告が参照するソースコード部分を、 deprecation() 自身ではなく deprecation() を呼び出した側にできます (というのも、前者の場合は警告メッセージの目的を台無しにしてしまうからです)。

source 引数が与えられた場合、これは ResourceWarning を発生させた破壊されたオブジェクトです。

バージョン 3.6 で変更: source 引数を追加しました。

warnings.warn_explicit(message, category, filename, lineno, module=None, registry=None, module_globals=None, source=None)

warn() の機能に対する低レベルのインタフェースで、メッセージ、警告カテゴリ、ファイル名および行番号、そしてオプションのモジュール名およびレジストリ情報 (モジュールの __warningregistry__ 辞書) を明示的に渡します。モジュール名は標準で .py が取り去られたファイル名になります; レジストリが渡されなかった場合、警告が抑制されることはありません。 message が文字列のとき、 categoryWarning のサブクラスでなければなりません。また messageWarning のインスタンスであってもよく、この場合 category は無視されます。

module_globals は、もし与えられるならば、警告が発せられるコードが使っているグローバル名前空間でなければなりません (この引数は zipfile やその他の非ファイルシステムのインポート元の中にあるモジュールのソースを表示することをサポートするためのものです)。

source 引数が与えられた場合、これは ResourceWarning を発生させた破壊されたオブジェクトです。

バージョン 3.6 で変更: source 引数を追加しました。

warnings.showwarning(message, category, filename, lineno, file=None, line=None)

警告をファイルに書き込みます。標準の実装では、 formatwarning(message, category, filename, lineno, line) を呼び出し、返された文字列を file に書き込みます。 file は標準では sys.stderr です。この関数は warnings.showwarning に任意の呼び出し可能オブジェクトを代入して置き換えることができます。 line は警告メッセージに含めるソースコードの1行です。 line が与えられない場合、 showwarning()filenamelineno から行を取得することを試みます。

warnings.formatwarning(message, category, filename, lineno, line=None)

警告を通常の方法で書式化します。返される文字列内には改行が埋め込まれている可能性があり、かつ文字列は改行で終端されています。 line は警告メッセージに含まれるソースコードの1行です。 line が渡されない場合、 formatwarning()filenamelineno から行の取得を試みます。

warnings.filterwarnings(action, message='', category=Warning, module='', lineno=0, append=False)

警告フィルタ仕様 のリストにエントリを一つ挿入します。標準ではエントリは先頭に挿入されます; append が真ならば、末尾に挿入されます。この関数は引数の型をチェックし、 message および module の正規表現をコンパイルしてから、これらをタプルにして警告フィルタのリストに挿入します。二つのエントリが特定の警告に合致した場合、リストの先頭に近い方のエントリが後方にあるエントリに優先します。引数が省略されると、標準ではすべてにマッチする値に設定されます。

warnings.simplefilter(action, category=Warning, lineno=0, append=False)

単純なエントリを 警告フィルタ仕様 のリストに挿入します。引数の意味は filterwarnings() と同じですが、この関数により挿入されるフィルタはカテゴリと行番号が一致していればすべてのモジュールのすべてのメッセージに合致しますので、正規表現は必要ありません。

warnings.resetwarnings()

警告フィルタをリセットします。これにより、 -W コマンドラインオプションによるもの simplefilter() 呼び出しによるものを含め、 filterwarnings() の呼び出しによる影響はすべて無効化されます。

利用可能なコンテキストマネージャー

class warnings.catch_warnings(*, record=False, module=None)

警告フィルタと showwarning() 関数をコピーし、終了時に復元するコンテキストマネージャーです。 record 引数が False (デフォルト値)だった場合、コンテキスト開始時には None を返します。もし recordTrue だった場合、リストを返します。このリストにはカスタムの showwarning() 関数(この関数は同時に sys.stdout への出力を抑制します)によってオブジェクトが継続的に追加されます。リストの中の各オブジェクトは、 showwarning() 関数の引数と同じ名前の属性を持っています。

module 引数は、保護したいフィルタを持つモジュールを取ります。 warnings を import して得られるモジュールの代わりに利用されます。この引数は、主に warnings モジュール自体をテストする目的で追加されました。

注釈

catch_warnings マネージャーは、モジュールの showwarning() 関数と内部のフィルタ仕様のリストを置き換え、その後復元することによって動作しています。これは、コンテキストマネージャーがグローバルな状態を変更していることを意味していて、したがってスレッドセーフではありません。