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
類的子類。
目前已定義了以下警告類別的類:
類 |
描述 |
---|---|
這是所有警告類別的基類。它是 |
|
The default category for |
|
已廢棄特性警告的基類,這些警告是為其他 Python 開發(fā)者準(zhǔn)備的(默認(rèn)會(huì)忽略,除非在 |
|
用于警告可疑語法的基類。 |
|
用于警告可疑運(yùn)行時(shí)特性的基類。 |
|
用于警告已廢棄特性的基類,這些警告是為 Python 應(yīng)用程序的最終用戶準(zhǔn)備的。 |
|
用于警告即將廢棄功能的基類(默認(rèn)忽略)。 |
|
導(dǎo)入模塊時(shí)觸發(fā)的警告的基類(默認(rèn)忽略)。 |
|
用于 Unicode 相關(guān)警告的基類。 |
|
Base category for warnings related to resource usage (ignored by default). |
在 3.7 版更改: 以前 DeprecationWarning
和 FutureWarning
是根據(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è)字符串,category 是Warning
的子類;或者*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()
將嘗試讀取由 filename 和 lineno 指定的行。
- 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ù)的類型,編譯 message 和 module 正則表達(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
。 如果 record 為True
,則返回一個(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 tosimplefilter()
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.