【導語】:在日常開發的過程中,當代碼報錯時,我們通常要不斷打印、閱讀traceback提示信息,來調試代碼,這篇文章介紹了如何實現一個Exception Hooks,使得traceback模塊的提示信息更加精確;同時還介紹了一些第三方庫,這些庫也提供了Exception Hooks的功能。
簡介
在日常開發中,我們的大部分時間都會花在閱讀traceback模塊信息以及調試代碼上。本文我們將改進traceback模塊,讓其中的提示信息更加簡潔準確。
基于這一目的,我們將會自定義Exception Hooks(異常處理鉤子),用來去除traceback中的冗余信息,只留下解決報錯所需的內容。此外,我還會介紹一些好用的第三方庫,你可以直接使用其中的Exception Hooks,來簡化traceback模塊。
Exception Hooks
假如程序的異常信息沒有被try/catch捕獲到,python解釋器就會調用sys.excepthook()函數,它會接收3個參數,分別是:type, value, traceback。這個函數也被稱為Exception Hook,會輸出程序的異常信息。
我們來看看下面這個例子:
importsys defexception_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}") #Exceptiontype和value print(f"{exc_type.__name__},Message:{exc_value}") sys.excepthook=exception_hook
在這個例子中,我們可以從traceback (tb)對象中獲取到異常信息出現的位置,位置信息包括:文件名(f_code.co_filename),函數/模塊名(f_code.co_name), 和行數(tb_lineno)。此外,我們可以使用exc_type和exc_value變量來獲取異常信息的內容。
當我們調用一個會產生錯誤的函數時,exception_hook會輸出如下內容:
defdo_stuff(): #寫一段會產生異常的代碼 raiseValueError("Someerrormessage") do_stuff() #Traceback: #File/home/some/path/exception_hooks.pyline22,in#ValueError,Message:Someerrormessage
上述例子提供了一部分異常信息,但要想獲取調試代碼所需的全部信息,并知道異常出現的時間及位置,我們還需要深入研究下traceback對象:
defexception_hook(exc_type,exc_value,tb): local_vars={} whiletb: 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"Localvariablesintopframe:{local_vars}") ... #File/home/some/path/exception_hooks.pyline41,in#File/home/some/path/exception_hooks.pyline7,indo_stuff #Localvariablesintopframe:{'some_var':'data'}
由上面的例子可以看出,traceback對象(tb)本質上是一個鏈表 - 存儲著所有出現的exceptions。因此可以使用tb_next對tb進行遍歷,并打印每一個異常的信息。在此基礎上,還可以使用tb_frame.f_locals屬性將變量輸出到console中,這有助于調試代碼。
使用traceback對象輸出異常信息是可行的,但是比較麻煩,此外輸出的信息可讀性較差。更方便的做法是使用traceback模塊,該模塊內置了許多提取異常信息的輔助函數。
目前我們已經介紹了Exception Hooks的基礎知識,接下來我們可以自定義一個exception hooks,并加入一些實用的特性。
自定義Exception Hooks
我們還可以讓異常信息自動存入文件中,在之后調試代碼的時候查看:
LOG_FILE_PATH="./some.log" FILE=open(LOG_FILE_PATH,mode="w") defexception_hook(exc_type,exc_value,tb): FILE.write("***Exception:*** ") traceback.print_exc(file=FILE) FILE.write(" ***Traceback:*** ") traceback.print_tb(tb,file=FILE) #***Exception:*** #NoneType:None # #***Traceback:*** #File"/home/some/path/exception_hooks.py",line82,in#do_stuff() #File"/home/some/path/exception_hooks.py",line7,indo_stuff #raiseValueError("Someerrormessage")
異常信息默認會存儲到stderr中,如果你想改變存儲位置,可以這樣做:
importlogging logging.basicConfig( level=logging.CRITICAL, format='[%(asctime)s]{%(pathname)s:%(lineno)d}%(levelname)s-%(message)s', datefmt='%H:%M:%S', stream=sys.stdout ) defexception_hook(exc_type,exc_value,exc_traceback): logging.critical("Uncaughtexception:",exc_info=(exc_type,exc_value,exc_traceback)) #[1733]{/home/some/path/exception_hooks.py:117}CRITICAL-Uncaughtexception: #Traceback(mostrecentcalllast): #File"/home/some/path/exception_hooks.py",line122,in#do_stuff() #File"/home/some/path/exception_hooks.py",line7,indo_stuff #raiseValueError("Someerrormessage") #ValueError:Someerrormessage
我們還可以給提示信息的某一部分設置顏色:
#pipinstallcolorama fromcoloramaimportinit,Fore init(autoreset=True)#Resetthecoloraftereveryprint defexception_hook(exc_type,exc_value,tb): local_vars={} whiletb: filename=tb.tb_frame.f_code.co_filename name=tb.tb_frame.f_code.co_name line_no=tb.tb_lineno #Prependdesiredcolor(e.g.RED)toline 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}Localvariablesintopframe:{local_vars}")
除了上面介紹的例子,你還可以輸出每一幀的局部變量,或者找到出現異常的行中所引用的變量。這些Exception Hooks已經很成熟了,相比于自定義Exception hooks,我建議你閱讀下其他開發者的源碼,學習下他們的設計思路。
輸出每一幀的局部變量[1]
找到出現異常的行中所引用的變量[2]
第三方庫中的Exception Hooks
自定義一個Exception Hook很有趣,但許多第三方庫已經實現這一功能了。與其自己造輪子,不如看看其他優秀的工具。
首先,我個人最喜歡的是Rich,可以直接用pip進行安裝,隨后導入使用。如果你只想在一個例子中使用,可以這樣做:python -m rich.traceback
#https://rich.readthedocs.io/en/latest/traceback.html #pipinstallrich #python-mrich.traceback fromrich.tracebackimportinstall install(show_locals=True) do_stuff()#RaisesValueError
better_exceptions也很受歡迎,我們需要先設置環境變量BETTER_EXCEPTIONS=1,再用pip安裝。此外,如果你的TERM變量不是xterm,還要把SUPPORTS_COLOR設置為True。
#https://github.com/Qix-/better-exceptions #pipinstallbetter_exceptions #exportBETTER_EXCEPTIONS=1 importbetter_exceptions better_exceptions.MAX_LENGTH=None #檢查你的TERM變量是否被設置為`xterm`,如果沒有執行以下操作 #Seeissue:https://github.com/Qix-/better-exceptions/issues/8 better_exceptions.SUPPORTS_COLOR=True better_exceptions.hook() do_stuff()#RaisesValueError
使用最方便的庫是pretty_errors,只需導入即可:
#https://github.com/onelivesleft/PrettyErrors/ #pipinstallpretty_errors importpretty_errors #如果你對默認配置滿意的話,則無需修改 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()
除了直接導入外,上面的代碼還顯示了該庫的一些可選配置。更多的配置可以查看這里:配置[3]
IPython的ultratb模塊
#https://ipython.readthedocs.io/en/stable/api/generated/IPython.core.ultratb.html #pipinstallipython importIPython.core.ultratb #AlsoColorTB,FormattedTB,ListTB,SyntaxTB sys.excepthook=IPython.core.ultratb.VerboseTB(color_scheme='Linux')#Othercolors:NoColor,LightBG,Neutral do_stuff()
stackprinter庫
#https://github.com/cknd/stackprinter #pipinstallstackprinter importstackprinter stackprinter.set_excepthook(style='darkbg2') do_stuff()
結論
本文我們學習了如何自定義Exception Hooks,但我更推薦使用第三方庫。你可以在本文介紹的第三方庫中任選一個喜歡的,用到項目中。需要注意的是使用自定義Exception Hooks可能會丟失某些關鍵信息,例如:本文中的某些例子中,輸出中缺少文件路徑,在遠程調試代碼這無疑很不方便,因此,需要謹慎使用。
審核編輯:湯梓紅
-
代碼
+關注
關注
30文章
4744瀏覽量
68345 -
python
+關注
關注
56文章
4782瀏覽量
84449
原文標題:漲知識!Python 的異常信息還能這樣展現
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論