当前位置: 首页 > 科技观察

增长知识!Python的异常信息也可以这样显示_0

时间:2023-03-17 23:10:11 科技观察

介绍在日常开发中,我们大部分时间都花在了阅读traceback模块信息和调试代码上。在本文中,我们将对traceback模块进行改进,让提示信息更加简洁准确。基于这个目的,我们会自定义ExceptionHooks(异常处理钩子)来去除traceback中的冗余信息,只留下解决错误所需要的内容。此外,我还会介绍一些有用的第三方库,你可以直接使用其中的ExceptionHooks来简化traceback模块。ExceptionHooks如果程序的异常信息没有被try/catch捕捉到,python解释器就会调用sys.excepthook()函数,该函数会接收3个参数,分别是:type,value,traceback。这个函数也叫ExceptionHook,会输出程序的异常信息。我们来看下面的例子:importsysdefexception_hook(exc_type,exc_value,tb):print('Traceback:')filename=tb.tb_frame.f_code.co_filenamename=tb.tb_frame.f_code.co_nameline_no=tb.tb_linenoprint(f"File{filename}line{line_no},in{name}")#异常类型和值print(f"{exc_type.__name__},Message:{exc_value}")sys.excepthook=exception_hook在这个例子中,我们可以从回溯(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:一些错误信息上面的例子提供了一些异常信息,但是要获取调试代码所需的所有信息,并知道何时何地异常发生了,我们还要深入traceback对象:defexception_hook(exc_type,exc_value,tb):local_vars={}whiletb:filename=tb.tb_frame.f_code.co_filenamename=tb.tb_frame.f_code.co_nameline_no=tb.tb_linenoprint(f"File{filename}line{line_no},in{name}")local_vars=tb.tb_frame.f_localstb=tb.tb_nextprint(f"顶部框架中的局部变量:{local_vars}")...#File/home/some/path/exception_hooks.pyline41,in#File/home/some/path/exception_hooks.pyline7,indo_stuff#Localvariablesintopframe:{'some_var':'data'}从上面的例子可以看出,回溯对象(tb)本质上是一个链表——存储所有发生的异常。所以可以使用tb_next遍历tb,打印每一次异常的信息。在此基础上,还可以使用tb_frame.f_locals属性将变量输出到控制台,有助于调试代码。使用traceback对象输出异常信息是可行的,但比较麻烦,输出信息的可读性差。使用traceback模块更方便,它内置了很多提取异常信息的辅助函数。既然我们已经介绍了ExceptionHooks的基础知识,那么我们可以自定义一个exceptionhooks并添加一些有用的功能。CustomExceptionHooks我们也可以让异常信息自动存储在文件中,然后在调试代码的时候查看:LOG_FILE_PATH="./some.log"FILE=open(LOG_FILE_PATH,mode="w")defexception_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",行82、在#do_stuff()#File"/home/some/path/exception_hooks.py",line7,indo_stuff#raiseValueError("Someerrormessage")异常信息默认会保存在stderr中,如果你想改变存储位置,你可以这样做:(levelname)s-%(message)s',datefmt='%H:%M:%S',stream=sys.stdout)defexception_hook(exc_type,exc_value,exc_traceback):logging.critical("未捕获异常:",exc_info=(exc_type,exc_value,exc_traceback))#[17:28:33]{/home/some/path/exception_hooks.py:117}CRITICAL-未捕获的异常:#Traceback(最近调用最后一次):#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我们也可以设置部分提示信息的颜色:#pipinstallcoloramafromcoloramaimportinit,Foreinit(autoreset=True)#在每次printdefexception_hook(exc_type,exc_value,tb)后重置颜色co_nameline_no=tb.tb_lineno#将所需的颜色(例如红色)添加到线条print(f"{Fore.RED}File{filename}line{line_no},in{name}")local_vars=tb.tb_frame.f_localstb=tb.tb_nextprint(f"{Fore.GREEN}Localvariablesintopframe:{local_vars}")除了上面介绍的例子,还可以输出每一帧的局部变量,或者找到行中引用的变量发生异常的地方这些ExceptionHooks非常成熟。相对于自定义Exceptionhooks,我建议你阅读其他开发者的源码,学习他们的设计思路。输出每一帧的局部变量[1]在异常发生的那一行找到引用的变量[2]第三方库中的ExceptionHooks自定义一个ExceptionHook很有意思,但是很多第三方库已经实现了这个功能。与其自己发明轮子,不如看看其他优秀的工具。首先,我个人最喜欢的是Rich,可以直接用pip安装,然后导入使用。如果你只想在一个例子中使用它,你可以这样做:python-mrich.traceback#https://rich.readthedocs.io/en/latest/traceback.html#pipinstallrich#python-mrich。tracebackfromrich.tracebackimportinstallinstall(show_locals=True)do_stuff()#RaisesValueErrorbetter_exceptions也很流行,我们需要在用pip安装前设置环境变量BETTER_EXCEPTIONS=1。此外,如果您的TERM变量不是xterm,请将SUPPORTS_COLOR设置为True。#https://github.com/Qix-/better-exceptions#pipinstallbetter_exceptions#exportBETTER_EXCEPTIONS=1importbetter_exceptionsbetter_exceptions.MAX_LENGTH=None#检查你的TERM变量是否设置为`xterm`,如果没有设置以下#查看问题:https://github.com/Qix-/better-exceptions/issues/8better_exceptions.SUPPORTS_COLOR=Truebetter_exceptions.hook()do_stuff()#RaisesValueError最方便使用的库是pretty_errors,只需导入它:#https://github.com/onelivesleft/PrettyErrors/#pipinstallpretty_errorsimportpretty_errors#如果你对默认配置满意,就不需要修改pretty_errors.configure(filename_display=pretty_errors.FILENAME_EXTENDED,line_number_first=True,display_link=True,line_colorers=preRED+'>'+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#pipinstallipythonimportIPython.core。ultratb#AlsoColorTB,FormattedTB,ListTB,SyntaxTBsys.excepthook=IPython.core.ultratb.VerboseTB(color_scheme='Linux')#其他颜色:NoColor,LightBG,Neutraldo_stuff()stackprinterlibrary#https://github.com/cknd/stackprinter#pipinstallstackprinterimportstackprinterstackprinter.set_excepthook(style='darkbg2')do_stuff()结论在本文中我们学习了如何自定义ExceptionHooks,但我建议使用第三方库。您可以选择本文介绍的第三方库之一,在项目中使用。需要注意的是,使用自定义的ExceptionHooks可能会丢失一些关键信息。例如,在本文的某些示例中,输出中缺少文件路径。这在远程调试代码时无疑是非常不方便的,因此需要谨慎使用。