warnings —— 警告信息的控制?

源代碼: Lib/warnings.py


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

Python 程序員可調用本模塊中定義的 warn() 函數來(lái)發(fā)布警告。(C 語(yǔ)言程序員則用 PyErr_WarnEx() ; 詳見(jiàn) 異常處理 )。

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

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

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

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

參見(jiàn)

利用 logging.captureWarnings() 可以采用標準的日志架構處理所有警告。

警告類(lèi)別?

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

雖然在技術(shù)上警告類(lèi)別屬于 內置異常,但也只是在此記錄一下而已,因為在概念上他們屬于警告機制的一部分。

通過(guò)對某個(gè)標準的警告類(lèi)別進(jìn)行派生,用戶(hù)代碼可以定義其他的警告類(lèi)別。 警告類(lèi)別必須是 Warning 類(lèi)的子類(lèi)。

目前已定義了以下警告類(lèi)別的類(lèi):

類(lèi)

描述

Warning

這是所有警告類(lèi)別的基類(lèi)。它是 Exception 的子類(lèi)。

UserWarning

The default category for warn().

DeprecationWarning

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

SyntaxWarning

用于警告可疑語(yǔ)法的基類(lèi)。

RuntimeWarning

用于警告可疑運行時(shí)特性的基類(lèi)。

FutureWarning

用于警告已廢棄特性的基類(lèi),這些警告是為 Python 應用程序的最終用戶(hù)準備的。

PendingDeprecationWarning

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

ImportWarning

導入模塊時(shí)觸發(fā)的警告的基類(lèi)(默認忽略)。

UnicodeWarning

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

BytesWarning

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

ResourceWarning

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

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

警告過(guò)濾器?

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

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

  • action 是以下字符串之一:

    處置

    "default"

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

    "error"

    將匹配警告轉換為異常

    "ignore"

    從不打印匹配的警告

    "always"

    總是打印匹配的警告

    "module"

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

    "once"

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

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

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

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

  • lineno 是個(gè)整數,發(fā)生警告的行號必須與之匹配,或為 0 表示與所有行號匹配。

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

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

警告過(guò)濾器的介紹?

警告過(guò)濾器由傳給 Python 解釋器的命令行 -W 選項和 PYTHONWARNINGS 環(huán)境變量初始化。解釋器在 sys.warningoptions 中保存了所有給出的參數,但不作解釋?zhuān)?a title="warnings: Issue warning messages and control their disposition." class="reference internal" href="#module-warnings">warnings 模塊在第一次導入時(shí)會(huì )解析這些參數(無(wú)效的選項被忽略,并會(huì )先向 sys.stderr 打印一條信息)。

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

action:message:category:module:line

這些字段的含義在 警告過(guò)濾器 中描述。當一行中列出多個(gè)過(guò)濾器時(shí)(如 PYTHONWARNINGS),過(guò)濾器間用逗號隔開(kāi),后面的優(yōu)先于前面的(因為是從左到右應用的,最近應用的過(guò)濾器優(yōu)先于前面的)。

常用的警告過(guò)濾器適用于所有的警告、特定類(lèi)別的警告、由特定模塊和包引發(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"

默認警告過(guò)濾器?

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

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

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

調試版本 中,默認警告過(guò)濾器的列表是空的。

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

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

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

重寫(xiě)默認的過(guò)濾器?

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

import sys

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

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

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__ 以外的命名空間運行用戶(hù)代碼的交互式開(kāi)發(fā)者,請確保 DeprecationWarning 在默認情況下是可見(jiàn)的,可采用如下代碼(這里 user_ns 是用于執行交互式輸入代碼的模塊):

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

暫時(shí)禁止警告?

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

import warnings

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

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

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

測試警告?

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

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)因為 once / default 規則而被引發(fā),那么無(wú)論設置什么過(guò)濾器,該條警告都不會(huì )再出現,除非該警告有關(guān)的注冊數據被清除。

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

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

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

在默認情況下,主要針對 Python 開(kāi)發(fā)者(而不是 Python 應用程序的最終用戶(hù))的警告類(lèi)別,會(huì )被忽略。

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

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

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

可用的函數?

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

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

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

這會(huì )讓警告指向 deprecation() 的調用者,而不是 deprecation() 本身的來(lái)源(因為后者會(huì )破壞引發(fā)警告的目的)。

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

在 3.6 版更改: 加入 source  參數。

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

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

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

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

在 3.6 版更改: 加入 source 參數。

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

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

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

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

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

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

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

警告過(guò)濾器種類(lèi) 列表中插入一條簡(jiǎn)單數據項。函數參數的含義與 filterwarnings() 相同,但不需要正則表達式,因為插入的過(guò)濾器總是匹配任何模塊中的任何信息,只要類(lèi)別和行號匹配即可。

warnings.resetwarnings()?

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

可用的上下文管理器?

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

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

module 參數代表一個(gè)模塊,當導入 warnings 時(shí),將被用于代替返回的模塊,其過(guò)濾器將被保護。該參數主要是為了測試 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 管理器的工作方式,是替換并隨后恢復模塊的 showwarning() 函數和內部的過(guò)濾器種類(lèi)列表。這意味著(zhù)上下文管理器將會(huì )修改全局狀態(tài),因此不是線(xiàn)程安全的。

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