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) |
描述 |
---|---|
這是所有警告類(lèi)別的基類(lèi)。它是 |
|
The default category for |
|
已廢棄特性警告的基類(lèi),這些警告是為其他 Python 開(kāi)發(fā)者準備的(默認會(huì )忽略,除非在 |
|
用于警告可疑語(yǔ)法的基類(lèi)。 |
|
用于警告可疑運行時(shí)特性的基類(lèi)。 |
|
用于警告已廢棄特性的基類(lèi),這些警告是為 Python 應用程序的最終用戶(hù)準備的。 |
|
用于警告即將廢棄功能的基類(lèi)(默認忽略)。 |
|
導入模塊時(shí)觸發(fā)的警告的基類(lèi)(默認忽略)。 |
|
用于 Unicode 相關(guān)警告的基類(lèi)。 |
|
Base category for warnings related to resource usage (ignored by default). |
在 3.7 版更改: 以前 DeprecationWarning
和 FutureWarning
是根據某個(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è)字符串,category 是Warning
的子類(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()
將嘗試讀取由 filename 和 lineno 指定的行。
- warnings.filterwarnings(action, message='', category=Warning, module='', lineno=0, append=False)?
在 警告過(guò)濾器種類(lèi) 列表中插入一條數據項。默認情況下,該數據項將被插到前面;如果 append 為 True,則會(huì )插到后面。這里會(huì )檢查參數的類(lèi)型,編譯 message 和 module 正則表達式,并將他們作為一個(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
。 如果 record 為True
,則返回一個(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 tosimplefilter()
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.