warnings
—— 警告信息的控制?
源代碼: Lib/warnings.py
通常以下情況會引發(fā)警告:提醒用戶注意程序中的某些情況,而這些情況(通常)還不值得觸發(fā)異常并終止程序。例如,當程序用到了某個過時的模塊時,就可能需要發(fā)出一條警告。
Python 程序員可調(diào)用本模塊中定義的 warn()
函數(shù)來發(fā)布警告。(C 語言程序員則用 PyErr_WarnEx()
; 詳見 異常處理 )。
警告信息通常會寫入 sys.stderr
,但可以靈活改變,從忽略所有警告到變成異常都可以。警告的處理方式可以依據(jù) 警告類型 、警告信息的文本和發(fā)出警告的源位置而進行變化。同一源位置重復出現(xiàn)的警告通常會被抑制。
控制警告信息有兩個階段:首先,每次引發(fā)警告時,決定信息是否要發(fā)出;然后,如果要發(fā)出信息,就用可由用戶設置的鉤子進行格式化并打印輸出。
警告過濾器 控制著是否發(fā)出警告信息,也即一系列的匹配規(guī)則和動作。調(diào)用 filterwarnings()
可將規(guī)則加入過濾器,調(diào)用 resetwarnings()
則可重置為默認狀態(tài)。
警告信息的打印輸出是通過調(diào)用 showwarning()
完成的,該函數(shù)可被重寫;默認的實現(xiàn)代碼是調(diào)用 formatwarning()
進行格式化,自己編寫的代碼也可以調(diào)用此格式化函數(shù)。
參見
利用 logging.captureWarnings()
可以采用標準的日志架構處理所有警告。
警告類別?
警告的類別由一些內(nèi)置的異常表示。這種分類有助于對警告信息進行分組過濾。
雖然在技術上警告類別屬于 內(nèi)置異常,但也只是在此記錄一下而已,因為在概念上他們屬于警告機制的一部分。
通過對某個標準的警告類別進行派生,用戶代碼可以定義其他的警告類別。 警告類別必須是 Warning
類的子類。
目前已定義了以下警告類別的類:
類 |
描述 |
---|---|
這是所有警告類別的基類。它是 |
|
The default category for |
|
已廢棄特性警告的基類,這些警告是為其他 Python 開發(fā)者準備的(默認會忽略,除非在 |
|
用于警告可疑語法的基類。 |
|
用于警告可疑運行時特性的基類。 |
|
用于警告已廢棄特性的基類,這些警告是為 Python 應用程序的最終用戶準備的。 |
|
用于警告即將廢棄功能的基類(默認忽略)。 |
|
導入模塊時觸發(fā)的警告的基類(默認忽略)。 |
|
用于 Unicode 相關警告的基類。 |
|
Base category for warnings related to resource usage (ignored by default). |
在 3.7 版更改: 以前 DeprecationWarning
和 FutureWarning
是根據(jù)某個功能是否完全刪除或改變其行為來區(qū)分的。現(xiàn)在是根據(jù)受眾和默認警告過濾器的處理方式來區(qū)分的。
警告過濾器?
警告過濾器控制著警告是否被忽略、顯示或轉(zhuǎn)為錯誤(觸發(fā)異常)。
從概念上講,警告過濾器維護著一個經(jīng)過排序的過濾器類別列表;任何具體的警告都會依次與列表中的每種過濾器進行匹配,直到找到一個匹配項;過濾器決定了匹配項的處理方式。每個列表項均為 ( action , message , category , module , lineno ) 格式的元組,其中:
action 是以下字符串之一:
值
處置
"default"
為發(fā)出警告的每個位置(模塊+行號)打印第一個匹配警告
"error"
將匹配警告轉(zhuǎn)換為異常
"ignore"
從不打印匹配的警告
"always"
總是打印匹配的警告
"module"
為發(fā)出警告的每個模塊打印第一次匹配警告(無論行號如何)
"once"
無論位置如何,僅打印第一次出現(xiàn)的匹配警告
message 是包含正則表達式的字符串,警告信息的開頭必須與之匹配。該表達式編譯時不區(qū)分大小寫。
category 是警告類別的類(
Warning
的子類),警告類別必須是其子類,才能匹配。module 是個字符串,包含了模塊名稱必須匹配的正則表達式。該表達式編譯時大小寫敏感。
lineno 是個整數(shù),發(fā)生警告的行號必須與之匹配,或為
0
表示與所有行號匹配。
由于 Warning
類是由內(nèi)置類 Exception
派生出來的,要把某個警告變成錯誤,只要觸發(fā)``category(message)`` 即可。
如果警告不匹配所有已注冊的過濾器,那就會應用 “default” 動作(正如其名)。
警告過濾器的介紹?
警告過濾器由傳給 Python 解釋器的命令行 -W
選項和 PYTHONWARNINGS
環(huán)境變量初始化。解釋器在 sys.warningoptions
中保存了所有給出的參數(shù),但不作解釋;warnings
模塊在第一次導入時會解析這些參數(shù)(無效的選項被忽略,并會先向 sys.stderr
打印一條信息)。
每個警告過濾器的設定格式為冒號分隔的字段序列:
action:message:category:module:line
這些字段的含義在 警告過濾器 中描述。當一行中列出多個過濾器時(如 PYTHONWARNINGS
),過濾器間用逗號隔開,后面的優(yōu)先于前面的(因為是從左到右應用的,最近應用的過濾器優(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"
默認警告過濾器?
Python 默認安裝了幾個警告過濾器,可以通過 -W
命令行參數(shù)、 PYTHONWARNINGS
環(huán)境變量及調(diào)用 filterwarnings()
進行覆蓋。
在常規(guī)發(fā)布的版本中,默認的警告過濾器包括(按優(yōu)先順序排列):
default::DeprecationWarning:__main__
ignore::DeprecationWarning
ignore::PendingDeprecationWarning
ignore::ImportWarning
ignore::ResourceWarning
在 調(diào)試版本 中,默認警告過濾器的列表是空的。
在 3.2 版更改: 除了 PendingDeprecationWarning
之外,DeprecationWarning
現(xiàn)在默認會被忽略。
在 3.7 版更改: DeprecationWarning
在被 __main__
中的代碼直接觸發(fā)時,默認會再次顯示。
在 3.7 版更改: 如果指定兩次 -b
,則 BytesWarning
不再出現(xiàn)在默認的過濾器列表中,而是通過 sys.warningoptions
進行配置。
重寫默認的過濾器?
Python 應用程序的開發(fā)人員可能希望在默認情況下向用戶隱藏 所有 Python級別的警告,而只在運行測試或其他調(diào)試時顯示這些警告。用于向解釋器傳遞過濾器配置的 sys.warningoptions
屬性可以作為一個標記,表示是否應該禁用警告:
import sys
if not sys.warnoptions:
import warnings
warnings.simplefilter("ignore")
建議 Python 代碼測試的開發(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__
以外的命名空間運行用戶代碼的交互式開發(fā)者,請確保 DeprecationWarning
在默認情況下是可見的,可采用如下代碼(這里 user_ns
是用于執(zhí)行交互式輸入代碼的模塊):
import warnings
warnings.filterwarnings("default", category=DeprecationWarning,
module=user_ns.get("__name__"))
暫時禁止警告?
如果明知正在使用會引起警告的代碼,比如某個廢棄函數(shù),但不想看到警告(即便警告已經(jīng)通過命令行作了顯式配置),那么可以使用 catch_warnings
上下文管理器來抑制警告。
import warnings
def fxn():
warnings.warn("deprecated", DeprecationWarning)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
fxn()
在上下文管理器中,所有的警告將被簡單地忽略。這樣就能使用已知的過時代碼而又不必看到警告,同時也不會限制警告其他可能不知過時的代碼。注意:只能保證在單線程應用程序中生效。如果兩個以上的線程同時使用 catch_warnings
上下文管理器,行為不可預知。
測試警告?
要測試由代碼引發(fā)的警告,請采用 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)
也可以用 error
取代 always
,讓所有的警告都成為異常。需要注意的是,如果某條警告已經(jīng)因為 once
/ default
規(guī)則而被引發(fā),那么無論設置什么過濾器,該條警告都不會再出現(xiàn),除非該警告有關的注冊數(shù)據(jù)被清除。
一旦上下文管理器退出,警告過濾器將恢復到剛進此上下文時的狀態(tài)。這樣在多次測試時可防止意外改變警告過濾器,從而導致不確定的測試結果。模塊中的 showwarning()
函數(shù)也被恢復到初始值。注意:這只能在單線程應用程序中得到保證。如果兩個以上的線程同時使用 catch_warnings
上下文管理器,行為未定義。
當測試多項操作會引發(fā)同類警告時,重點是要確保每次操作都會觸發(fā)新的警告(比如,將警告設置為異常并檢查操作是否觸發(fā)異常,檢查每次操作后警告列表的長度是否有增加,否則就在每次新操作前將以前的警告列表項刪除)。
為新版本的依賴關系更新代碼?
在默認情況下,主要針對 Python 開發(fā)者(而不是 Python 應用程序的最終用戶)的警告類別,會被忽略。
值得注意的是,這個“默認忽略”的列表包含 DeprecationWarning
(適用于每個模塊,除了 __main__
),這意味著開發(fā)人員應該確保在測試代碼時應將通常忽略的警告顯示出來,以便未來破壞性 API 變化時及時收到通知(無論是在標準庫還是第三方包)。
理想情況下,代碼會有一個合適的測試套件,在運行測試時會隱含地啟用所有警告(由 unittest
模塊提供的測試運行程序就是如此)。
在不太理想的情況下,可以通過向 Python 解釋器傳入 -Wd
(這是 -W default
的簡寫) 或設置環(huán)境變量 PYTHONWARNINGS=default
來檢查應用程序是否用到了已棄用的接口。 這樣可以啟用對所有警告的默認處理操作,包括那些默認忽略的警告。 要改變遇到警告后執(zhí)行的動作,可以改變傳給 -W
的參數(shù) (例如 -W error
)。 請參閱 -W
旗標來了解更多的細節(jié)。
可用的函數(shù)?
- warnings.warn(message, category=None, stacklevel=1, source=None)?
引發(fā)警告、忽略或者觸發(fā)異常。 如果給出 category 參數(shù),則必須是 警告類別類 ;默認為
UserWarning
。 或者 message 可為Warning
的實例,這時 category 將被忽略,轉(zhuǎn)而采用message.__class__
。 在這種情況下,錯誤信息文本將是str(message)
。 如果某條警告被 警告過濾器 改成了錯誤,本函數(shù)將觸發(fā)一條異常。 參數(shù) stacklevel 可供 Python 包裝函數(shù)使用,比如:def deprecation(message): warnings.warn(message, DeprecationWarning, stacklevel=2)
這會讓警告指向
deprecation()
的調(diào)用者,而不是deprecation()
本身的來源(因為后者會破壞引發(fā)警告的目的)。source 是發(fā)出
ResourceWarning
的被銷毀對象。在 3.6 版更改: 加入 source 參數(shù)。
- warnings.warn_explicit(message, category, filename, lineno, module=None, registry=None, module_globals=None, source=None)?
這是
warn()
函數(shù)的底層接口,顯式傳入消息、類別、文件名和行號,以及可選的模塊名和注冊表(應為模塊的__warningregistry__
字典)。 模塊名稱默認為去除了.py
的文件名;如果未傳遞注冊表,警告就不會被抑制。 message 必須是個字符串,category 是Warning
的子類;或者*message* 可為Warning
的實例,且 category 將被忽略。module_globals 應為發(fā)出警告的代碼所用的全局命名空間。(該參數(shù)用于從 zip 文件或其他非文件系統(tǒng)導入模塊時顯式源碼)。
source 是發(fā)出
ResourceWarning
的被銷毀對象。在 3.6 版更改: 加入 source 參數(shù)。
- warnings.showwarning(message, category, filename, lineno, file=None, line=None)?
將警告信息寫入文件。默認的實現(xiàn)代碼是調(diào)用``formatwarning(message, category, filename, lineno, line)`` 并將結果字符串寫入 file ,默認文件為
sys.stderr
。通過將任何可調(diào)用對象賦給warnings.showwarning
可替換掉該函數(shù)。line 是要包含在警告信息中的一行源代碼;如果未提供 line,showwarning()
將嘗試讀取由*filename* 和 lineno 指定的行。
- warnings.formatwarning(message, category, filename, lineno, line=None)?
以標準方式格式化一條警告信息。將返回一個字符串,可能包含內(nèi)嵌的換行符,并以換行符結束。如果未提供 line,
formatwarning()
將嘗試讀取由 filename 和 lineno 指定的行。
- warnings.filterwarnings(action, message='', category=Warning, module='', lineno=0, append=False)?
在 警告過濾器種類 列表中插入一條數(shù)據(jù)項。默認情況下,該數(shù)據(jù)項將被插到前面;如果 append 為 True,則會插到后面。這里會檢查參數(shù)的類型,編譯 message 和 module 正則表達式,并將他們作為一個元組插入警告過濾器的列表中。如果兩者都與某種警告匹配,那么靠近列表前面的數(shù)據(jù)項就會覆蓋后面的項。省略的參數(shù)默認匹配任意值。
- warnings.simplefilter(action, category=Warning, lineno=0, append=False)?
在 警告過濾器種類 列表中插入一條簡單數(shù)據(jù)項。函數(shù)參數(shù)的含義與
filterwarnings()
相同,但不需要正則表達式,因為插入的過濾器總是匹配任何模塊中的任何信息,只要類別和行號匹配即可。
- warnings.resetwarnings()?
重置警告過濾器。這將丟棄之前對
filterwarnings()
的所有調(diào)用,包括-W
命令行選項和對simplefilter()
的調(diào)用效果。
可用的上下文管理器?
- class warnings.catch_warnings(*, record=False, module=None, action=None, category=Warning, lineno=0, append=False)?
該上下文管理器會復制警告過濾器和
showwarning()
函數(shù),并在退出時恢復。 如果 record 參數(shù)是False
(默認),則在進入時會返回None
。 如果 record 為True
,則返回一個列表,列表由自定義showwarning()
函數(shù)所用對象逐步填充(該函數(shù)還會抑制sys.stdout
的輸出)。 列表中每個對象的屬性與showwarning()
的參數(shù)名稱相同。module 參數(shù)代表一個模塊,當導入
warnings
時,將被用于代替返回的模塊,其過濾器將被保護。該參數(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
管理器的工作方式,是替換并隨后恢復模塊的showwarning()
函數(shù)和內(nèi)部的過濾器種類列表。這意味著上下文管理器將會修改全局狀態(tài),因此不是線程安全的。在 3.11 版更改: Added the action, category, lineno, and append parameters.