warnings --- 警告控制

原始碼:Lib/warnings.py


警告訊息通常會在需要提醒使用者程式中的某些狀況時發出,而該狀況(通常)不至於需要引發例外並終止程式。舉例來說,當程式使用一個過時的模組時,就可能會需要發出警告。

Python 程式設計師可以透過呼叫此模組中定義的warn() 函式來發出警告。(C 語言的程式設計師則使用PyErr_WarnEx();詳情請參閱例外處理)。

警告訊息通常會被寫入sys.stderr,但它們的處理方式可以被彈性地更改,從忽略所有警告到將它們轉為例外都可以。警告的處理方式會根據警告類別、警告訊息的文本,以及發出警告的原始碼位置而有所不同。在同一個原始碼位置重複出現的特定警告通常會被抑制。

警告控制有兩個階段:首先,每當發出一個警告時,會先決定是否該發出訊息;接著,如果要發出訊息,它會使用一個可由使用者設定的掛鉤 (hook) 來格式化並印出。

是否要發出警告訊息是由警告過濾器 所控制,它是一連串的匹配規則和動作。可以透過呼叫filterwarnings() 來新增規則到過濾器中,並透過呼叫resetwarnings() 將其重設為預設狀態。

警告訊息的印出是透過呼叫showwarning() 來完成,而它可以被覆寫;此函式的預設實作會透過呼叫formatwarning() 來格式化訊息,它也可以被自訂的實作使用。

也參考

logging.captureWarnings() 允許你使用標準的 logging 基礎設施來處理所有警告。

警告類別

有許多內建的例外代表警告類別。這種分類方式對於能夠過濾掉特定群組的警告很有用。

雖然這些在技術上是內建例外,但它們被記錄在這裡,因為從概念上來說,它們屬於警告機制的一部分。

使用者程式碼可以透過繼承其中一個標準警告類別來定義額外的警告類別。一個警告類別必須永遠是Warning 類別的子類別。

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

類別

描述

Warning

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

UserWarning

warn() 的預設類別。

DeprecationWarning

關於已棄用功能的警告的基底類別,當這些警告是針對其他 Python 開發者時(預設為忽略,除非由__main__ 中的程式碼觸發)。

SyntaxWarning

Base category for warnings about dubioussyntactic features (typically emitted whencompiling Python source code, and hencemay not be suppressed by runtime filters)

RuntimeWarning

關於可疑 runtime 功能的警告的基底類別。

FutureWarning

關於已棄用功能的警告的基底類別,當這些警告是針對以 Python 編寫的應用程式的終端使用者時。

PendingDeprecationWarning

關於未來將被棄用的功能的警告的基底類別(預設為忽略)。

ImportWarning

在引入模組過程中觸發的警告的基底類別(預設為忽略)。

UnicodeWarning

與 Unicode 相關的警告的基底類別。

BytesWarning

bytesbytearray 相關的警告的基底類別。

ResourceWarning

與資源使用相關的警告的基底類別(預設為忽略)。

在 3.7 版的變更:在過去,DeprecationWarningFutureWarning 是根據一個功能是被完全移除還是改變其行為來區分的。它們現在是根據其目標受眾以及預設警告過濾器處理它們的方式來區分。

警告過濾器

警告過濾器控制警告是被忽略、顯示,還是轉為錯誤(引發一個例外)。

從概念上講,警告過濾器維護一個有序的過濾器規格串列;任何特定的警告都會依次與串列中的每個過濾器規格進行比對,直到找到匹配項;過濾器決定了匹配項的處理方式。每個條目都是一個 (action,message,category,module,lineno) 形式的元組,其中:

  • action 是以下字串之一:

    處理方式

    "default"

    為發出警告的每個位置(模組 + 行號)印出第一次出現的匹配警告

    "error"

    將匹配的警告轉為例外

    "ignore"

    永不印出匹配的警告

    "always"

    總是印出匹配的警告

    "all"

    "always" 的別名

    "module"

    為發出警告的每個模組印出第一次出現的匹配警告(不論行號)

    "once"

    只印出第一次出現的匹配警告,不論位置

  • message 是一個包含正規表示式的字串,警告訊息的開頭必須與其匹配(不區分大小寫)。在-WPYTHONWARNINGS 中,message 是一個字面字串,警告訊息的開頭必須包含該字串(不區分大小寫),並忽略message 開頭或結尾的任何空白字元。

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

  • module 是一個包含正規表示式的字串,完整限定模組名稱的開頭必須與其匹配(區分大小寫)。在-WPYTHONWARNINGS 中,module 是一個字面字串,完整限定模組名稱必須與其相等(區分大小寫),並忽略module 開頭或結尾的任何空白字元。

  • lineno 是一個整數,發出警告的行號必須與其匹配,或者為0 以匹配所有行號。

由於Warning 類別衍生自內建的Exception 類別,要將警告轉為錯誤,我們只需引發category(message)

如果一個警告被回報且不匹配任何已註冊的過濾器,則會套用 "default" 動作(因此得名)。

重複警告的抑制標準

抑制重複警告的過濾器會套用以下標準來判斷一個警告是否被視為重複:

  • "default":只有當 (message,category,module,lineno) 都相同時,警告才被視為重複。

  • "module":如果 (message,category,module) 相同,則警告被視為重複,忽略行號。

  • "once":如果 (message,category) 相同,則警告被視為重複,忽略模組和行號。

描述警告過濾器

警告過濾器由傳遞給 Python 直譯器命令列的-W 選項和PYTHONWARNINGS 環境變數初始化。直譯器會將所有提供條目的引數未經直譯地儲存在sys.warnoptions 中;warnings 模組在首次引入時會剖析這些引數(無效選項會被忽略,並在向sys.stderr 印出一條訊息後)。

個別的警告過濾器被指定為一系列由冒號分隔的欄位:

action:message:category:module:line

這些欄位中每一個的含義都如警告過濾器 中所述。當在單一行中列出多個過濾器時(例如PYTHONWARNINGS),個別的過濾器會以逗號分隔,且後面列出的過濾器優先於前面列出的過濾器(因為它們是從左到右應用的,而最近應用的過濾器優先於較早的過濾器)。

常用的警告過濾器適用於所有警告、特定類別的警告,或由特定模組或套件引發的警告。一些範例如下:

default# 顯示所有警告(即使是預設忽略的警告)ignore# 忽略所有警告error# 將所有警告轉換為錯誤error::ResourceWarning# 將 ResourceWarning 訊息視為錯誤default::DeprecationWarning# 顯示 DeprecationWarning 訊息ignore,default:::mymodule# 只回報由 "mymodule" 觸發的警告error:::mymodule# 將 "mymodule" 中的警告轉換為錯誤

預設警告過濾器

預設情況下,Python 會安裝數個警告過濾器,這些過濾器可以被-W 命令列選項、PYTHONWARNINGS 環境變數以及對filterwarnings() 的呼叫所覆寫。

在常規的發行建置中,預設的警告過濾器有以下條目(按優先順序排列):

default::DeprecationWarning:__main__ignore::DeprecationWarningignore::PendingDeprecationWarningignore::ImportWarningignore::ResourceWarning

偵錯建置 中,預設警告過濾器串列是空的。

在 3.2 版的變更:除了PendingDeprecationWarning 之外,DeprecationWarning 現在也預設被忽略。

在 3.7 版的變更:當直接由__main__ 中的程式碼觸發時,DeprecationWarning 會再次預設顯示。

在 3.7 版的變更:BytesWarning 不再出現在預設過濾器串列中,而是在指定-b 兩次時,透過sys.warnoptions 進行設定。

覆寫預設過濾器

以 Python 編寫的應用程式的開發者可能希望預設對其使用者隱藏所有 Python 層級的警告,並且只在執行測試或以其他方式處理應用程式時才顯示它們。用於將過濾器設定傳遞給直譯器的sys.warnoptions 屬性可以用作一個標記,以指示是否應停用警告:

importsysifnotsys.warnoptions:importwarningswarnings.simplefilter("ignore")

建議 Python 程式碼的測試執行器開發者,應確保在預設情況下,為受測程式碼顯示所有 警告,可使用如下程式碼:

importsysifnotsys.warnoptions:importos,warningswarnings.simplefilter("default")# 在此行程中更改過濾器os.environ["PYTHONWARNINGS"]="default"# 也會影響子行程

最後,建議在__main__ 以外的命名空間中執行使用者程式碼的互動式 shell 開發者,應確保DeprecationWarning 訊息預設為可見,可使用如下程式碼(其中user_ns 是用於執行互動式輸入程式碼的模組):

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

暫時抑制警告

如果你正在使用的程式碼,你知道它會引發一個警告(例如一個已棄用的函式),但你不想看到這個警告(即使警告已透過命令列明確設定),那麼可以使用catch_warnings 情境管理器來抑制該警告:

importwarningsdeffxn():warnings.warn("deprecated",DeprecationWarning)withwarnings.catch_warnings():warnings.simplefilter("ignore")fxn()

在情境管理器中,所有警告都將被直接忽略。這允許你在使用已知的已棄用程式碼時不必看到警告,同時又不會抑制其他可能不知道自己正在使用已棄用程式碼的程式碼所發出的警告。

備註

See情境管理器的並行安全性 for details on theconcurrency-safety of thecatch_warnings context manager whenused in programs using multiple threads or async functions.

測試警告

要測試程式碼引發的警告,請使用catch_warnings 情境管理器。透過它,你可以暫時改變警告過濾器以方便你的測試。例如,執行以下操作來捕獲所有引發的警告以進行檢查:

importwarningsdeffxn():warnings.warn("deprecated",DeprecationWarning)withwarnings.catch_warnings(record=True)asw:# 讓所有警告總是會被觸發。warnings.simplefilter("always")# 觸發一個警告。fxn()# 驗證一些事情assertlen(w)==1assertissubclass(w[-1].category,DeprecationWarning)assert"deprecated"instr(w[-1].message)

也可以使用error 而非always 來讓所有警告都變成例外。需要注意的一點是,如果一個警告因為once/default 規則已經被引發過,那麼無論設定什麼過濾器,這個警告都不會再出現,除非與該警告相關的警告註冊表已被清除。

一旦情境管理器退出,警告過濾器就會恢復到進入情境時的狀態。這可以防止測試在不同測試之間以意想不到的方式更改警告過濾器,從而導致不確定的測試結果。

備註

See情境管理器的並行安全性 for details on theconcurrency-safety of thecatch_warnings context manager whenused in programs using multiple threads or async functions.

在測試多個會引發同類警告的操作時,重要的是要以一種能確認每個操作都在引發新警告的方式來進行測試(例如,將警告設定為引發例外,並檢查操作是否引發例外;檢查警告串列的長度在每次操作後是否持續增加;或者在每次新操作前從警告串列中刪除先前的條目)。

為新版相依套件更新程式碼

主要針對 Python 開發者(而非以 Python 編寫的應用程式的終端使用者)的警告類別預設會被忽略。

值得注意的是,這個「預設忽略」串列包括DeprecationWarning(除了__main__ 以外的所有模組),這意味著開發者應該確保在測試他們的程式碼時,讓通常被忽略的警告可見,以便及時收到未來 API 破壞性變更的通知(無論是在標準函式庫還是第三方套件中)。

在理想情況下,程式碼會有一個合適的測試套件,而測試執行器會在執行測試時負責隱式地啟用所有警告(unittest 模組提供的測試執行器就是這樣做的)。

在較不理想的情況下,可以透過將-Wd 傳遞給 Python 直譯器(這是-Wdefault 的簡寫)或在環境中設定PYTHONWARNINGS=default 來檢查應用程式是否使用了已棄用的介面。這會為所有警告啟用預設處理,包括那些預設被忽略的警告。要更改對遇到的警告所採取的動作,你可以更改傳遞給-W 的引數(例如-Werror)。有關更多可能性的詳細資訊,請參閱-W 旗標。

可用的函式

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

發出一個警告,或者可能忽略它或引發一個例外。category 引數(如果提供)必須是一個警告類別;它預設為UserWarning。或者,message 可以是一個Warning 實例,在這種情況下category 將被忽略,並使用message.__class__。在這種情況下,訊息文本將是str(message)。如果發出的特定警告被警告過濾器 轉為錯誤,此函式會引發一個例外。stacklevel 引數可以被以 Python 編寫的包裝函式使用,如下所示:

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

這會讓警告指向deprecated_api 的呼叫者,而不是deprecated_api 本身的原始碼(因為後者會違背警告訊息的目的)。

skip_file_prefixes 關鍵字引數可用於指示在計算堆疊層級時應忽略哪些堆疊框。當你希望警告總是在套件外部的呼叫點出現,而固定的stacklevel 不適用於所有呼叫路徑或難以維護時,這會很有用。如果提供,它必須是一個字串的元組。當提供前綴時,stacklevel 會被隱式地覆寫為max(2,stacklevel)。要讓警告歸因於目前套件外部的呼叫者,你可以這樣寫:

# example/lower.py_warn_skips=(os.path.dirname(__file__),)defone_way(r_luxury_yacht=None,t_wobbler_mangrove=None):ifr_luxury_yacht:warnings.warn("Please migrate to t_wobbler_mangrove=.",skip_file_prefixes=_warn_skips)# example/higher.pyfrom.importlowerdefanother_way(**kw):lower.one_way(**kw)

這使得警告只會從存在於example 套件之外的呼叫程式碼中,指向example.lower.one_way()example.higher.another_way() 的呼叫點。

source(如果提供)是發出ResourceWarning 的已銷毀物件。

在 3.6 版的變更:新增source 參數。

在 3.12 版的變更:新增skip_file_prefixes

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

這是一個warn() 功能的低階介面,明確傳入訊息、類別、檔名和行號,以及可選的模組名稱和註冊表(應為模組的__warningregistry__ 字典)。模組名稱預設為去掉.py 的檔名;如果沒有傳入註冊表,警告將永遠不會被抑制。message 必須是字串,category 必須是Warning 的子類別,或者message 可以是Warning 的實例,在這種情況下category 將被忽略。

module_globals(如果提供)應該是發出警告的程式碼所使用的全域命名空間。(此引數用於支援顯示在 zip 檔案或其他非檔案系統引入來源中找到的模組的原始碼)。

source(如果提供)是發出ResourceWarning 的已銷毀物件。

在 3.6 版的變更:新增source 參數。

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

將警告寫入一個檔案。預設實作會呼叫formatwarning(message,category,filename,lineno,line) 並將結果字串寫入file,預設為sys.stderr。你可以透過賦值給warnings.showwarning 來用任何可呼叫物件取代此函式。line 是要包含在警告訊息中的一行原始碼;如果未提供lineshowwarning() 將嘗試讀取由filenamelineno 指定的行。

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

以標準方式格式化警告。這會回傳一個可能包含嵌入換行符並以換行符結尾的字串。line 是要包含在警告訊息中的一行原始碼;如果未提供lineformatwarning() 將嘗試讀取由filenamelineno 指定的行。

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

警告過濾器規格 串列中插入一個條目。預設情況下,條目會插入到最前面;如果append 為 true,則會插入到最後面。這會檢查引數的型別,編譯messagemodule 的正規表示式,並將它們作為一個元組插入到警告過濾器串列中。如果兩個條目都匹配一個特定的警告,那麼串列中較靠前的條目會覆寫較後面的條目。省略的引數預設為一個能匹配所有內容的值。

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

警告過濾器規格 串列中插入一個簡單的條目。函式參數的含義與filterwarnings() 相同,但不需要正規表示式,因為只要類別和行號匹配,插入的過濾器總是會匹配任何模組中的任何訊息。

warnings.resetwarnings()

重設警告過濾器。這會捨棄所有先前對filterwarnings() 的呼叫效果,包括-W 命令列選項和對simplefilter() 的呼叫。

@warnings.deprecated(msg,*,category=DeprecationWarning,stacklevel=1)

用來指示一個類別、函式或重載 (overload) 已被棄用的裝飾器。

當此裝飾器應用於一個物件時,在使用該物件時,可能會在 runtime 發出棄用警告。靜態型別檢查器 也會在使用已棄用物件時產生診斷訊息。

用法:

fromwarningsimportdeprecatedfromtypingimportoverload@deprecated("改用 B")classA:pass@deprecated("改用 g")deff():pass@overload@deprecated("對 int 的支援已棄用")defg(x:int)->int:...@overloaddefg(x:str)->int:...

category 指定的警告將在使用已棄用物件時於 runtime 發出。對於函式,這發生在呼叫時;對於類別,發生在實例化和建立子類別時。如果categoryNone,則在 runtime 不會發出警告。stacklevel 決定了警告發出的位置。如果它是1(預設值),警告會在已棄用物件的直接呼叫者處發出;如果更高,它會在堆疊的更上層發出。靜態型別檢查器的行為不受categorystacklevel 引數的影響。

The deprecation message passed to the decorator is saved in the__deprecated__ attribute on the decorated object.If applied to an overload, the decoratormust be after the@~typing.overload decoratorfor the attribute to exist on the overload as returned bytyping.get_overloads().

在 3.13 版被加入:參閱PEP 702

可用的情境管理器

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

一個情境管理器,它會複製並在退出時恢復警告過濾器和showwarning() 函式。如果record 引數為False(預設值),情境管理器在進入時回傳None。如果recordTrue,則會回傳一個串列,該串列會由自訂的showwarning() 函式(該函式也會抑制對sys.stdout 的輸出)所看到的物件逐步填充。串列中的每個物件都具有與showwarning() 的引數同名的屬性。

module 引數接受一個模組,用以取代預設的warnings 模組,而該(被傳入)模組的過濾器將會受到保護。此引數主要用於測試warnings 模組本身。

如果action 引數不是None,則其餘引數會傳遞給simplefilter(),就像在進入情境時立即呼叫它一樣。

有關categorylineno 參數的含義,請參閱警告過濾器

備註

See情境管理器的並行安全性 for details on theconcurrency-safety of thecatch_warnings context manager whenused in programs using multiple threads or async functions.

在 3.11 版的變更:新增actioncategorylinenoappend 參數。

情境管理器的並行安全性

The behavior ofcatch_warnings context manager depends on thesys.flags.context_aware_warnings flag. If the flag is true, thecontext manager behaves in a concurrent-safe fashion and otherwise not.Concurrent-safe means that it is both thread-safe and safe to use withinasyncio coroutines and tasks. Being thread-safe meansthat behavior is predictable in a multi-threaded program. The flag defaultsto true for free-threaded builds and false otherwise.

If thecontext_aware_warnings flag is false, thencatch_warnings will modify the global attributes of thewarnings module. This is not safe if used within a concurrent program(using multiple threads or using asyncio coroutines). For example, if twoor more threads use thecatch_warnings class at the same time, thebehavior is undefined.

If the flag is true,catch_warnings will not modify globalattributes and will instead use aContextVar tostore the newly established warning filtering state. A context variableprovides thread-local storage and it makes the use ofcatch_warningsthread-safe.

Therecord parameter of the context handler also behaves differentlydepending on the value of the flag. Whenrecord is true and the flag isfalse, the context manager works by replacing and then later restoring themodule'sshowwarning() function. That is not concurrent-safe.

Whenrecord is true and the flag is true, theshowwarning() functionis not replaced. Instead, the recording status is indicated by an internalproperty in the context variable. In this case, theshowwarning()function will not be restored when exiting the context handler.

Thecontext_aware_warnings flag can be set the-Xcontext_aware_warnings command-line option or by thePYTHON_CONTEXT_AWARE_WARNINGS environment variable.

備註

It is likely that most programs that desire thread-safebehaviour of the warnings module will also want to set thethread_inherit_context flag to true. That flagcauses threads created bythreading.Thread to startwith a copy of the context variables from the thread startingit. When true, the context established bycatch_warningsin one thread will also apply to new threads started by it. If false,new threads will start with an empty warnings context variable,meaning that any filtering that was established by acatch_warnings context manager will no longer be active.

在 3.14 版的變更:Added thesys.flags.context_aware_warnings flag and the use of acontext variable forcatch_warnings if the flag is true. Previousversions of Python acted as if the flag was always set to false.