Python異常信息的不同展現(xiàn)方法總結(jié)
簡介
在日常開發(fā)中,我們的大部分時間都會花在閱讀traceback模塊信息以及調(diào)試代碼上。本文我們將改進(jìn)traceback模塊,讓其中的提示信息更加簡潔準(zhǔn)確。
基于這一目的,我們將會自定義Exception Hooks(異常處理鉤子),用來去除traceback中的冗余信息,只留下解決報錯所需的內(nèi)容。此外,我還會介紹一些好用的第三方庫,你可以直接使用其中的Exception Hooks,來簡化traceback模塊。
Exception Hooks
假如程序的異常信息沒有被try/catch捕獲到,python解釋器就會調(diào)用sys.excepthook()函數(shù),它會接收3個參數(shù),分別是:type, value, traceback。這個函數(shù)也被稱為Exception Hook,會輸出程序的異常信息。
我們來看看下面這個例子:
import sys def exception_hook(exc_type, exc_value, tb): print('Traceback:') filename = tb.tb_frame.f_code.co_filename name = tb.tb_frame.f_code.co_name line_no = tb.tb_lineno print(f"File {filename} line {line_no}, in {name}") # Exception type 和 value print(f"{exc_type.__name__}, Message: {exc_value}") sys.excepthook = exception_hook
在這個例子中,我們可以從traceback (tb)對象中獲取到異常信息出現(xiàn)的位置,位置信息包括:文件名(f_code.co_filename),函數(shù)/模塊名(f_code.co_name), 和行數(shù)(tb_lineno)。此外,我們可以使用exc_type和exc_value變量來獲取異常信息的內(nèi)容。
當(dāng)我們調(diào)用一個會產(chǎn)生錯誤的函數(shù)時,exception_hook會輸出如下內(nèi)容:
def do_stuff(): # 寫一段會產(chǎn)生異常的代碼 raise ValueError("Some error message") do_stuff() # Traceback: # File /home/some/path/exception_hooks.py line 22, in <module> # ValueError, Message: Some error message
上述例子提供了一部分異常信息,但要想獲取調(diào)試代碼所需的全部信息,并知道異常出現(xiàn)的時間及位置,我們還需要深入研究下traceback對象:
def exception_hook(exc_type, exc_value, tb): local_vars = {} while tb: filename = tb.tb_frame.f_code.co_filename name = tb.tb_frame.f_code.co_name line_no = tb.tb_lineno print(f"File {filename} line {line_no}, in {name}") local_vars = tb.tb_frame.f_locals tb = tb.tb_next print(f"Local variables in top frame: {local_vars}") ... # File /home/some/path/exception_hooks.py line 41, in <module> # File /home/some/path/exception_hooks.py line 7, in do_stuff # Local variables in top frame: {'some_var': 'data'}
由上面的例子可以看出,traceback對象(tb)本質(zhì)上是一個鏈表 - 存儲著所有出現(xiàn)的exceptions。因此可以使用tb_next對tb進(jìn)行遍歷,并打印每一個異常的信息。在此基礎(chǔ)上,還可以使用tb_frame.f_locals屬性將變量輸出到console中,這有助于調(diào)試代碼。
使用traceback對象輸出異常信息是可行的,但是比較麻煩,此外輸出的信息可讀性較差。更方便的做法是使用traceback模塊,該模塊內(nèi)置了許多提取異常信息的輔助函數(shù)。
目前我們已經(jīng)介紹了Exception Hooks的基礎(chǔ)知識,接下來我們可以自定義一個exception hooks,并加入一些實用的特性。
自定義Exception Hooks
我們還可以讓異常信息自動存入文件中,在之后調(diào)試代碼的時候查看:
LOG_FILE_PATH = "./some.log" FILE = open(LOG_FILE_PATH, mode="w") def exception_hook(exc_type, exc_value, tb): FILE.write("*** Exception: ***\n") traceback.print_exc(file=FILE) FILE.write("\n*** Traceback: ***\n") traceback.print_tb(tb, file=FILE) # *** Exception: *** # NoneType: None # # *** Traceback: *** # File "/home/some/path/exception_hooks.py", line 82, in <module> # do_stuff() # File "/home/some/path/exception_hooks.py", line 7, in do_stuff # raise ValueError("Some error message")
異常信息默認(rèn)會存儲到stderr中,如果你想改變存儲位置,可以這樣做:
import logging logging.basicConfig( level=logging.CRITICAL, format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', datefmt='%H:%M:%S', stream=sys.stdout ) def exception_hook(exc_type, exc_value, exc_traceback): logging.critical("Uncaught exception:", exc_info=(exc_type, exc_value, exc_traceback)) # [17:28:33] {/home/some/path/exception_hooks.py:117} CRITICAL - Uncaught exception: # Traceback (most recent call last): # File "/home/some/path/exception_hooks.py", line 122, in <module> # do_stuff() # File "/home/some/path/exception_hooks.py", line 7, in do_stuff # raise ValueError("Some error message") # ValueError: Some error message
我們還可以給提示信息的某一部分設(shè)置顏色:
# pip install colorama from colorama import init, Fore init(autoreset=True) # Reset the color after every print def exception_hook(exc_type, exc_value, tb): local_vars = {} while tb: filename = tb.tb_frame.f_code.co_filename name = tb.tb_frame.f_code.co_name line_no = tb.tb_lineno # Prepend desired color (e.g. RED) to line print(f"{Fore.RED}File {filename} line {line_no}, in {name}") local_vars = tb.tb_frame.f_locals tb = tb.tb_next print(f"{Fore.GREEN}Local variables in top frame: {local_vars}")
除了上面介紹的例子,你還可以輸出每一幀的局部變量,或者找到出現(xiàn)異常的行中所引用的變量。這些Exception Hooks已經(jīng)很成熟了,相比于自定義Exception hooks,我建議你閱讀下其他開發(fā)者的源碼,學(xué)習(xí)下他們的設(shè)計思路。
- 輸出每一幀的局部變量[1]
- 找到出現(xiàn)異常的行中所引用的變量[2]
第三方庫中的Exception Hooks
自定義一個Exception Hook很有趣,但許多第三方庫已經(jīng)實現(xiàn)這一功能了。與其自己造輪子,不如看看其他優(yōu)秀的工具。
首先,我個人最喜歡的是Rich,可以直接用pip進(jìn)行安裝,隨后導(dǎo)入使用。如果你只想在一個例子中使用,可以這樣做:python -m rich.traceback
# https://rich.readthedocs.io/en/latest/traceback.html # pip install rich # python -m rich.traceback from rich.traceback import install install(show_locals=True) do_stuff() # Raises ValueError
better_exceptions也很受歡迎,我們需要先設(shè)置環(huán)境變量BETTER_EXCEPTIONS=1,再用pip安裝。此外,如果你的TERM變量不是xterm,還要把SUPPORTS_COLOR設(shè)置為True。
# https://github.com/Qix-/better-exceptions # pip install better_exceptions # export BETTER_EXCEPTIONS=1 import better_exceptions better_exceptions.MAX_LENGTH = None # 檢查你的 TERM 變量是否被設(shè)置為 `xterm`, 如果沒有執(zhí)行以下操作 # See issue: https://github.com/Qix-/better-exceptions/issues/8 better_exceptions.SUPPORTS_COLOR = True better_exceptions.hook() do_stuff() # Raises ValueError
使用最方便的庫是pretty_errors,只需導(dǎo)入即可:
# https://github.com/onelivesleft/PrettyErrors/ # pip install pretty_errors import pretty_errors # 如果你對默認(rèn)配置滿意的話,則無需修改 pretty_errors.configure( filename_display = pretty_errors.FILENAME_EXTENDED, line_number_first = True, display_link = True, line_color = pretty_errors.RED + '> ' + pretty_errors.default_config.line_color, code_color = ' ' + pretty_errors.default_config.line_color, truncate_code = True, display_locals = True ) do_stuff()
除了直接導(dǎo)入外,上面的代碼還顯示了該庫的一些可選配置。更多的配置可以查看這里:配置[3]
IPython的ultratb模塊
# https://ipython.readthedocs.io/en/stable/api/generated/IPython.core.ultratb.html # pip install ipython import IPython.core.ultratb # Also ColorTB, FormattedTB, ListTB, SyntaxTB sys.excepthook = IPython.core.ultratb.VerboseTB(color_scheme='Linux') # Other colors: NoColor, LightBG, Neutral do_stuff()
stackprinter庫
# https://github.com/cknd/stackprinter # pip install stackprinter import stackprinter stackprinter.set_excepthook(style='darkbg2') do_stuff()
結(jié)論
本文我們學(xué)習(xí)了如何自定義Exception Hooks,但我更推薦使用第三方庫。你可以在本文介紹的第三方庫中任選一個喜歡的,用到項目中。需要注意的是使用自定義Exception Hooks可能會丟失某些關(guān)鍵信息,例如:本文中的某些例子中,輸出中缺少文件路徑,在遠(yuǎn)程調(diào)試代碼這無疑很不方便,因此,需要謹(jǐn)慎使用。
到此這篇關(guān)于Python異常信息的不同展現(xiàn)方法總結(jié)的文章就介紹到這了,更多相關(guān)Python異常信息內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python一行代碼識別發(fā)票并保存Excel示例詳解
這篇文章主要為大家介紹了Python一行代碼識別發(fā)票并保存Excel示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Python中用append()連接后多出一列Unnamed的解決
Python中用append()連接后多出一列Unnamed的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01python使用篩選法計算小于給定數(shù)字的所有素數(shù)
這篇文章主要為大家詳細(xì)介紹了python使用篩選法計算小于給定數(shù)字的所有素數(shù),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03python三種數(shù)據(jù)結(jié)構(gòu)及13種創(chuàng)建方法總結(jié)
拿Python來說,數(shù)據(jù)結(jié)構(gòu)的概念也是超級重要,不同的數(shù)據(jù)結(jié)構(gòu),有著不同的函數(shù),供我們調(diào)用,接下來,我們分別來介紹字符串、列表、字典的創(chuàng)建方法2021-09-09