0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何自定義Exception Hooks

Linux愛好者 ? 來源:Python開發(fā)者 ? 作者:南風(fēng)草木香 ? 2022-10-21 12:01 ? 次閱讀

【導(dǎo)語】:在日常開發(fā)的過程中,當(dāng)代碼報錯時,我們通常要不斷打印、閱讀traceback提示信息,來調(diào)試代碼,這篇文章介紹了如何實現(xiàn)一個Exception Hooks,使得traceback模塊的提示信息更加精確;同時還介紹了一些第三方庫,這些庫也提供了Exception Hooks的功能。

簡介

在日常開發(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,會輸出程序的異常信息。

我們來看看下面這個例子:

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)對象中獲取到異常信息出現(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)容:

defdo_stuff():
#寫一段會產(chǎn)生異常的代碼
raiseValueError("Someerrormessage")

do_stuff()

#Traceback:
#File/home/some/path/exception_hooks.pyline22,in
#ValueError,Message:Someerrormessage

上述例子提供了一部分異常信息,但要想獲取調(diào)試代碼所需的全部信息,并知道異常出現(xiàn)的時間及位置,我們還需要深入研究下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)本質(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")

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")

異常信息默認(rèn)會存儲到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

我們還可以給提示信息的某一部分設(shè)置顏色:

#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}")

除了上面介紹的例子,你還可以輸出每一幀的局部變量,或者找到出現(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
#pipinstallrich
#python-mrich.traceback

fromrich.tracebackimportinstall
install(show_locals=True)

do_stuff()#RaisesValueError

c74ced06-50f4-11ed-a3b6-dac502259ad0.png

better_exceptions也很受歡迎,我們需要先設(shè)置環(huán)境變量BETTER_EXCEPTIONS=1,再用pip安裝。此外,如果你的TERM變量不是xterm,還要把SUPPORTS_COLOR設(shè)置為True。

#https://github.com/Qix-/better-exceptions
#pipinstallbetter_exceptions
#exportBETTER_EXCEPTIONS=1

importbetter_exceptions
better_exceptions.MAX_LENGTH=None
#檢查你的TERM變量是否被設(shè)置為`xterm`,如果沒有執(zhí)行以下操作
#Seeissue:https://github.com/Qix-/better-exceptions/issues/8
better_exceptions.SUPPORTS_COLOR=True
better_exceptions.hook()

do_stuff()#RaisesValueError

c774f5f8-50f4-11ed-a3b6-dac502259ad0.png

使用最方便的庫是pretty_errors,只需導(dǎo)入即可:

#https://github.com/onelivesleft/PrettyErrors/
#pipinstallpretty_errors

importpretty_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]

c79b00e0-50f4-11ed-a3b6-dac502259ad0.png

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()

c7b100ca-50f4-11ed-a3b6-dac502259ad0.png

stackprinter庫

#https://github.com/cknd/stackprinter
#pipinstallstackprinter
importstackprinter
stackprinter.set_excepthook(style='darkbg2')

do_stuff()

c7c87a34-50f4-11ed-a3b6-dac502259ad0.png

結(jié)論

本文我們學(xué)習(xí)了如何自定義Exception Hooks,但我更推薦使用第三方庫。你可以在本文介紹的第三方庫中任選一個喜歡的,用到項目中。需要注意的是使用自定義Exception Hooks可能會丟失某些關(guān)鍵信息,例如:本文中的某些例子中,輸出中缺少文件路徑,在遠(yuǎn)程調(diào)試代碼這無疑很不方便,因此,需要謹(jǐn)慎使用。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4671

    瀏覽量

    67765
  • python
    +關(guān)注

    關(guān)注

    53

    文章

    4753

    瀏覽量

    84077

原文標(biāo)題:漲知識!Python 的異常信息還能這樣展現(xiàn)

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    HarmonyOS開發(fā)實例:【自定義Emitter】

    使用[Emitter]實現(xiàn)事件的訂閱和發(fā)布,使用[自定義彈窗]設(shè)置廣告信息。
    的頭像 發(fā)表于 04-14 11:37 ?880次閱讀
    HarmonyOS開發(fā)實例:【<b class='flag-5'>自定義</b>Emitter】

    TP5自定義異常的處理方法

    處理器,而 set_exception_handler 可以設(shè)置用戶自定義的異常處理函數(shù):function ExceptionHandler($e) {echo "Exception
    發(fā)表于 09-20 09:05

    自定義函數(shù)測試學(xué)習(xí)工程

    自定義函數(shù)測試學(xué)習(xí)工程
    發(fā)表于 07-01 16:37 ?5次下載

    SOPC中自定義外設(shè)和自定義指令性能分析

    SOPC中自定義外設(shè)和自定義指令性能分析 NiosII是一個建立在FPGA上的嵌入式軟核處理器,靈活性很強(qiáng)。作為體現(xiàn)NiosII靈活性精髓的兩個最主要方面,自
    發(fā)表于 03-29 15:12 ?1557次閱讀
    SOPC中<b class='flag-5'>自定義</b>外設(shè)和<b class='flag-5'>自定義</b>指令性能分析

    1602自定義字符

    1602液晶能夠顯示自定義字符,能夠根據(jù)讀者的具體情況顯示自定義字符。
    發(fā)表于 01-20 15:43 ?1次下載

    JAVA教程之自定義光標(biāo)

    JAVA教程之自定義光標(biāo),很好的學(xué)習(xí)資料。
    發(fā)表于 03-31 11:13 ?7次下載

    C#教程之自定義屏保

    C#教程之自定義屏保,很好的C#資料,快來學(xué)習(xí)吧。
    發(fā)表于 04-20 09:59 ?7次下載

    RTWconfigurationguide基于模型設(shè)計—自定義

    基于模型設(shè)計—自定義目標(biāo)系統(tǒng)配置指南,RTW自動代碼生成相關(guān)資料。
    發(fā)表于 05-17 16:41 ?3次下載

    PDH網(wǎng)管盤 自定義字節(jié)

    PDH網(wǎng)管盤 自定義字節(jié)
    發(fā)表于 12-26 22:13 ?0次下載

    如何在android設(shè)備上安裝自定義rom

    完成后,請執(zhí)行相同的操作,但不要選擇自定義rom,而是選擇間隙。安裝間隙之前需要使用一些自定義rom,您可以從自定義rom的開發(fā)人員網(wǎng)頁上了解,如果他們沒有提及任何相關(guān)內(nèi)容,只需在自定義
    的頭像 發(fā)表于 11-05 10:48 ?4986次閱讀

    自定義視圖組件教程案例

    自定義組件 1.自定義組件-particles(粒子效果) 2.自定義組件- pulse(脈沖button效果) 3.自定義組件-progress(progress效果) 4.
    發(fā)表于 04-08 10:48 ?14次下載

    ArkUI如何自定義彈窗(eTS)

    自定義彈窗其實也是比較簡單的,通過CustomDialogController類就可以顯示自定義彈窗。
    的頭像 發(fā)表于 08-31 08:24 ?1949次閱讀

    labview自定義控件

    labview自定義精美控件
    發(fā)表于 05-15 16:46 ?16次下載

    自定義算子開發(fā)

    一個完整的自定義算子應(yīng)用過程包括注冊算子、算子實現(xiàn)、含自定義算子模型轉(zhuǎn)換和運行含自定義op模型四個階段。在大多數(shù)情況下,您的模型應(yīng)該可以通過使用hb_mapper工具完成轉(zhuǎn)換并順利部署到地平線芯片上……
    的頭像 發(fā)表于 04-07 16:11 ?2508次閱讀
    <b class='flag-5'>自定義</b>算子開發(fā)

    labview超快自定義控件制作和普通自定義控件制作

    labview超快自定義控件制作和普通自定義控件制作
    發(fā)表于 08-21 10:32 ?11次下載