【导语】:在日常开发的过程中,当代码报错时,我们通常要不断打印、阅读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文章
4741浏览量
68324 -
python
+关注
关注
55文章
4778浏览量
84439
原文标题:涨知识!Python 的异常信息还能这样展现
文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论