warnings —— 警告信息的控制?

源代碼: Lib/warnings.py


通常以下情況會(huì)引發(fā)警告:提醒用戶注意程序中的某些情況,而這些情況(通常)還不值得觸發(fā)異常并終止程序。例如,當(dāng)程序用到了某個(gè)過時(shí)的模塊時(shí),就可能需要發(fā)出一條警告。

Python 程序員可調(diào)用本模塊中定義的 warn() 函數(shù)來發(fā)布警告。(C 語言程序員則用 PyErr_WarnEx() ; 詳見 異常處理 )。

警告信息通常會(huì)寫入 sys.stderr ,但可以靈活改變,從忽略所有警告到變成異常都可以。警告的處理方式可以依據(jù) 警告類型 、警告信息的文本和發(fā)出警告的源位置而進(jìn)行變化。同一源位置重復(fù)出現(xiàn)的警告通常會(huì)被抑制。

控制警告信息有兩個(gè)階段:首先,每次引發(fā)警告時(shí),決定信息是否要發(fā)出;然后,如果要發(fā)出信息,就用可由用戶設(shè)置的鉤子進(jìn)行格式化并打印輸出。

警告過濾器 控制著是否發(fā)出警告信息,也即一系列的匹配規(guī)則和動(dòng)作。調(diào)用 filterwarnings() 可將規(guī)則加入過濾器,調(diào)用 resetwarnings() 則可重置為默認(rèn)狀態(tài)。

警告信息的打印輸出是通過調(diào)用 showwarning() 完成的,該函數(shù)可被重寫;默認(rèn)的實(shí)現(xiàn)代碼是調(diào)用 formatwarning() 進(jìn)行格式化,自己編寫的代碼也可以調(diào)用此格式化函數(shù)。

參見

利用 logging.captureWarnings() 可以采用標(biāo)準(zhǔn)的日志架構(gòu)處理所有警告。

警告類別?

警告的類別由一些內(nèi)置的異常表示。這種分類有助于對(duì)警告信息進(jìn)行分組過濾。

雖然在技術(shù)上警告類別屬于 內(nèi)置異常,但也只是在此記錄一下而已,因?yàn)樵诟拍钌纤麄儗儆诰鏅C(jī)制的一部分。

通過對(duì)某個(gè)標(biāo)準(zhǔn)的警告類別進(jìn)行派生,用戶代碼可以定義其他的警告類別。 警告類別必須是 Warning 類的子類。

目前已定義了以下警告類別的類:

描述

Warning

這是所有警告類別的基類。它是 Exception 的子類。

UserWarning

The default category for warn().

DeprecationWarning

已廢棄特性警告的基類,這些警告是為其他 Python 開發(fā)者準(zhǔn)備的(默認(rèn)會(huì)忽略,除非在 __main__ 中用代碼觸發(fā))。

SyntaxWarning

用于警告可疑語法的基類。

RuntimeWarning

用于警告可疑運(yùn)行時(shí)特性的基類。

FutureWarning

用于警告已廢棄特性的基類,這些警告是為 Python 應(yīng)用程序的最終用戶準(zhǔn)備的。

PendingDeprecationWarning

用于警告即將廢棄功能的基類(默認(rèn)忽略)。

ImportWarning

導(dǎo)入模塊時(shí)觸發(fā)的警告的基類(默認(rèn)忽略)。

UnicodeWarning

用于 Unicode 相關(guān)警告的基類。

BytesWarning

bytesbytearray 相關(guān)警告的基類。

ResourceWarning

Base category for warnings related to resource usage (ignored by default).

在 3.7 版更改: 以前 DeprecationWarningFutureWarning 是根據(jù)某個(gè)功能是否完全刪除或改變其行為來區(qū)分的?,F(xiàn)在是根據(jù)受眾和默認(rèn)警告過濾器的處理方式來區(qū)分的。

警告過濾器?

警告過濾器控制著警告是否被忽略、顯示或轉(zhuǎn)為錯(cuò)誤(觸發(fā)異常)。

從概念上講,警告過濾器維護(hù)著一個(gè)經(jīng)過排序的過濾器類別列表;任何具體的警告都會(huì)依次與列表中的每種過濾器進(jìn)行匹配,直到找到一個(gè)匹配項(xiàng);過濾器決定了匹配項(xiàng)的處理方式。每個(gè)列表項(xiàng)均為 ( action , message , category , module , lineno ) 格式的元組,其中:

  • action 是以下字符串之一:

    處置

    "default"

    為發(fā)出警告的每個(gè)位置(模塊+行號(hào))打印第一個(gè)匹配警告

    "error"

    將匹配警告轉(zhuǎn)換為異常

    "ignore"

    從不打印匹配的警告

    "always"

    總是打印匹配的警告

    "module"

    為發(fā)出警告的每個(gè)模塊打印第一次匹配警告(無論行號(hào)如何)

    "once"

    無論位置如何,僅打印第一次出現(xiàn)的匹配警告

  • message 是包含正則表達(dá)式的字符串,警告信息的開頭必須與之匹配。該表達(dá)式編譯時(shí)不區(qū)分大小寫。

  • category 是警告類別的類(Warning 的子類),警告類別必須是其子類,才能匹配。

  • module 是個(gè)字符串,包含了模塊名稱必須匹配的正則表達(dá)式。該表達(dá)式編譯時(shí)大小寫敏感。

  • lineno 是個(gè)整數(shù),發(fā)生警告的行號(hào)必須與之匹配,或?yàn)?0 表示與所有行號(hào)匹配。

由于 Warning 類是由內(nèi)置類 Exception 派生出來的,要把某個(gè)警告變成錯(cuò)誤,只要觸發(fā)``category(message)`` 即可。

如果警告不匹配所有已注冊(cè)的過濾器,那就會(huì)應(yīng)用 “default” 動(dòng)作(正如其名)。

警告過濾器的介紹?

警告過濾器由傳給 Python 解釋器的命令行 -W 選項(xiàng)和 PYTHONWARNINGS 環(huán)境變量初始化。解釋器在 sys.warningoptions 中保存了所有給出的參數(shù),但不作解釋;warnings 模塊在第一次導(dǎo)入時(shí)會(huì)解析這些參數(shù)(無效的選項(xiàng)被忽略,并會(huì)先向 sys.stderr 打印一條信息)。

每個(gè)警告過濾器的設(shè)定格式為冒號(hào)分隔的字段序列:

action:message:category:module:line

這些字段的含義在 警告過濾器 中描述。當(dāng)一行中列出多個(gè)過濾器時(shí)(如 PYTHONWARNINGS),過濾器間用逗號(hào)隔開,后面的優(yōu)先于前面的(因?yàn)槭菑淖蟮接覒?yīng)用的,最近應(yīng)用的過濾器優(yōu)先于前面的)。

常用的警告過濾器適用于所有的警告、特定類別的警告、由特定模塊和包引發(fā)的警告。下面是一些例子:

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"

默認(rèn)警告過濾器?

Python 默認(rèn)安裝了幾個(gè)警告過濾器,可以通過 -W 命令行參數(shù)、 PYTHONWARNINGS 環(huán)境變量及調(diào)用 filterwarnings() 進(jìn)行覆蓋。

在常規(guī)發(fā)布的版本中,默認(rèn)的警告過濾器包括(按優(yōu)先順序排列):

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

調(diào)試版本 中,默認(rèn)警告過濾器的列表是空的。

在 3.2 版更改: 除了 PendingDeprecationWarning 之外,DeprecationWarning 現(xiàn)在默認(rèn)會(huì)被忽略。

在 3.7 版更改: DeprecationWarning 在被 __main__ 中的代碼直接觸發(fā)時(shí),默認(rèn)會(huì)再次顯示。

在 3.7 版更改: 如果指定兩次 -b,則 BytesWarning 不再出現(xiàn)在默認(rèn)的過濾器列表中,而是通過 sys.warningoptions 進(jìn)行配置。

重寫默認(rèn)的過濾器?

Python 應(yīng)用程序的開發(fā)人員可能希望在默認(rèn)情況下向用戶隱藏 所有 Python級(jí)別的警告,而只在運(yùn)行測試或其他調(diào)試時(shí)顯示這些警告。用于向解釋器傳遞過濾器配置的 sys.warningoptions 屬性可以作為一個(gè)標(biāo)記,表示是否應(yīng)該禁用警告:

import sys

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

建議 Python 代碼測試的開發(fā)者使用如下代碼,以確保被測代碼默認(rèn)顯示 所有 警告:

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

最后,建議在 __main__ 以外的命名空間運(yùn)行用戶代碼的交互式開發(fā)者,請(qǐng)確保 DeprecationWarning 在默認(rèn)情況下是可見的,可采用如下代碼(這里 user_ns 是用于執(zhí)行交互式輸入代碼的模塊):

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

暫時(shí)禁止警告?

如果明知正在使用會(huì)引起警告的代碼,比如某個(gè)廢棄函數(shù),但不想看到警告(即便警告已經(jīng)通過命令行作了顯式配置),那么可以使用 catch_warnings 上下文管理器來抑制警告。

import warnings

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

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

在上下文管理器中,所有的警告將被簡單地忽略。這樣就能使用已知的過時(shí)代碼而又不必看到警告,同時(shí)也不會(huì)限制警告其他可能不知過時(shí)的代碼。注意:只能保證在單線程應(yīng)用程序中生效。如果兩個(gè)以上的線程同時(shí)使用 catch_warnings 上下文管理器,行為不可預(yù)知。

測試警告?

要測試由代碼引發(fā)的警告,請(qǐng)采用 catch_warnings 上下文管理器。有了它,就可以臨時(shí)改變警告過濾器以方便測試。例如,以下代碼可捕獲所有的警告以便查看:

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)

也可以用 error 取代 always ,讓所有的警告都成為異常。需要注意的是,如果某條警告已經(jīng)因?yàn)?once / default 規(guī)則而被引發(fā),那么無論設(shè)置什么過濾器,該條警告都不會(huì)再出現(xiàn),除非該警告有關(guān)的注冊(cè)數(shù)據(jù)被清除。

一旦上下文管理器退出,警告過濾器將恢復(fù)到剛進(jìn)此上下文時(shí)的狀態(tài)。這樣在多次測試時(shí)可防止意外改變警告過濾器,從而導(dǎo)致不確定的測試結(jié)果。模塊中的 showwarning() 函數(shù)也被恢復(fù)到初始值。注意:這只能在單線程應(yīng)用程序中得到保證。如果兩個(gè)以上的線程同時(shí)使用 catch_warnings 上下文管理器,行為未定義。

當(dāng)測試多項(xiàng)操作會(huì)引發(fā)同類警告時(shí),重點(diǎn)是要確保每次操作都會(huì)觸發(fā)新的警告(比如,將警告設(shè)置為異常并檢查操作是否觸發(fā)異常,檢查每次操作后警告列表的長度是否有增加,否則就在每次新操作前將以前的警告列表項(xiàng)刪除)。

為新版本的依賴關(guān)系更新代碼?

在默認(rèn)情況下,主要針對(duì) Python 開發(fā)者(而不是 Python 應(yīng)用程序的最終用戶)的警告類別,會(huì)被忽略。

值得注意的是,這個(gè)“默認(rèn)忽略”的列表包含 DeprecationWarning (適用于每個(gè)模塊,除了 __main__),這意味著開發(fā)人員應(yīng)該確保在測試代碼時(shí)應(yīng)將通常忽略的警告顯示出來,以便未來破壞性 API 變化時(shí)及時(shí)收到通知(無論是在標(biāo)準(zhǔn)庫還是第三方包)。

理想情況下,代碼會(huì)有一個(gè)合適的測試套件,在運(yùn)行測試時(shí)會(huì)隱含地啟用所有警告(由 unittest 模塊提供的測試運(yùn)行程序就是如此)。

在不太理想的情況下,可以通過向 Python 解釋器傳入 -Wd (這是 -W default 的簡寫) 或設(shè)置環(huán)境變量 PYTHONWARNINGS=default 來檢查應(yīng)用程序是否用到了已棄用的接口。 這樣可以啟用對(duì)所有警告的默認(rèn)處理操作,包括那些默認(rèn)忽略的警告。 要改變遇到警告后執(zhí)行的動(dòng)作,可以改變傳給 -W 的參數(shù) (例如 -W error)。 請(qǐng)參閱 -W 旗標(biāo)來了解更多的細(xì)節(jié)。

可用的函數(shù)?

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

引發(fā)警告、忽略或者觸發(fā)異常。 如果給出 category 參數(shù),則必須是 警告類別類 ;默認(rèn)為 UserWarning。 或者 message 可為 Warning 的實(shí)例,這時(shí) category 將被忽略,轉(zhuǎn)而采用 message.__class__。 在這種情況下,錯(cuò)誤信息文本將是 str(message)。 如果某條警告被 警告過濾器 改成了錯(cuò)誤,本函數(shù)將觸發(fā)一條異常。 參數(shù) stacklevel 可供 Python 包裝函數(shù)使用,比如:

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

這會(huì)讓警告指向 deprecation() 的調(diào)用者,而不是 deprecation() 本身的來源(因?yàn)楹笳邥?huì)破壞引發(fā)警告的目的)。

source 是發(fā)出 ResourceWarning 的被銷毀對(duì)象。

在 3.6 版更改: 加入 source  參數(shù)。

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

這是 warn() 函數(shù)的底層接口,顯式傳入消息、類別、文件名和行號(hào),以及可選的模塊名和注冊(cè)表(應(yīng)為模塊的 __warningregistry__ 字典)。 模塊名稱默認(rèn)為去除了 .py 的文件名;如果未傳遞注冊(cè)表,警告就不會(huì)被抑制。 message 必須是個(gè)字符串,categoryWarning 的子類;或者*message* 可為 Warning 的實(shí)例,且 category 將被忽略。

module_globals 應(yīng)為發(fā)出警告的代碼所用的全局命名空間。(該參數(shù)用于從 zip 文件或其他非文件系統(tǒng)導(dǎo)入模塊時(shí)顯式源碼)。

source 是發(fā)出 ResourceWarning 的被銷毀對(duì)象。

在 3.6 版更改: 加入 source 參數(shù)。

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

將警告信息寫入文件。默認(rèn)的實(shí)現(xiàn)代碼是調(diào)用``formatwarning(message, category, filename, lineno, line)`` 并將結(jié)果字符串寫入 file ,默認(rèn)文件為 sys.stderr。通過將任何可調(diào)用對(duì)象賦給 warnings.showwarning 可替換掉該函數(shù)。line 是要包含在警告信息中的一行源代碼;如果未提供 line,showwarning() 將嘗試讀取由*filename* 和 lineno 指定的行。

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

以標(biāo)準(zhǔn)方式格式化一條警告信息。將返回一個(gè)字符串,可能包含內(nèi)嵌的換行符,并以換行符結(jié)束。如果未提供 line,formatwarning() 將嘗試讀取由 filenamelineno 指定的行。

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

警告過濾器種類 列表中插入一條數(shù)據(jù)項(xiàng)。默認(rèn)情況下,該數(shù)據(jù)項(xiàng)將被插到前面;如果 append 為 True,則會(huì)插到后面。這里會(huì)檢查參數(shù)的類型,編譯 messagemodule 正則表達(dá)式,并將他們作為一個(gè)元組插入警告過濾器的列表中。如果兩者都與某種警告匹配,那么靠近列表前面的數(shù)據(jù)項(xiàng)就會(huì)覆蓋后面的項(xiàng)。省略的參數(shù)默認(rèn)匹配任意值。

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

警告過濾器種類 列表中插入一條簡單數(shù)據(jù)項(xiàng)。函數(shù)參數(shù)的含義與 filterwarnings() 相同,但不需要正則表達(dá)式,因?yàn)椴迦氲倪^濾器總是匹配任何模塊中的任何信息,只要類別和行號(hào)匹配即可。

warnings.resetwarnings()?

重置警告過濾器。這將丟棄之前對(duì) filterwarnings() 的所有調(diào)用,包括 -W 命令行選項(xiàng)和對(duì) simplefilter() 的調(diào)用效果。

可用的上下文管理器?

class warnings.catch_warnings(*, record=False, module=None, action=None, category=Warning, lineno=0, append=False)?

該上下文管理器會(huì)復(fù)制警告過濾器和 showwarning() 函數(shù),并在退出時(shí)恢復(fù)。 如果 record 參數(shù)是 False (默認(rèn)),則在進(jìn)入時(shí)會(huì)返回 None。 如果 recordTrue,則返回一個(gè)列表,列表由自定義 showwarning() 函數(shù)所用對(duì)象逐步填充(該函數(shù)還會(huì)抑制 sys.stdout 的輸出)。 列表中每個(gè)對(duì)象的屬性與 showwarning() 的參數(shù)名稱相同。

module 參數(shù)代表一個(gè)模塊,當(dāng)導(dǎo)入 warnings 時(shí),將被用于代替返回的模塊,其過濾器將被保護(hù)。該參數(shù)主要是為了測試 warnings 模塊自身。

If the action argument is not None, the remaining arguments are passed to simplefilter() as if it were called immediately on entering the context.

備注

catch_warnings 管理器的工作方式,是替換并隨后恢復(fù)模塊的 showwarning() 函數(shù)和內(nèi)部的過濾器種類列表。這意味著上下文管理器將會(huì)修改全局狀態(tài),因此不是線程安全的。

在 3.11 版更改: Added the action, category, lineno, and append parameters.