In daily development, most of our time is spent reading traceback module information and debugging code. In this article, we will improve the traceback module to make the prompt information more concise and accurate.
Based on this purpose, we will customize Exception Hooks (exception handling hooks) to remove redundant information in the traceback, leaving only the content required to resolve the error. In addition, I will also introduce some useful third-party libraries, and you can directly use the Exception Hooks in them to simplify the traceback module.
If the exception information of the program is not captured by try/catch, the python interpreter will call the sys.excepthook() function, which will receive 3 parameters, respectively: type, value, traceback. This function is also called Exception Hook and will output the exception information of the program.
Let’s take a look at the following example:
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
In this example, we can get the location where the exception information occurs from the traceback (tb) object. The location information includes: File name (f_code.co_filename), function/module name (f_code.co_name), and line number (tb_lineno). In addition, we can use the exc_type and exc_value variables to obtain the content of the exception information.
When we call a function that generates an error, exception_hook will output the following content:
def do_stuff(): # 写一段会产生异常的代码 raise ValueError("Some error message") do_stuff() # Traceback: # File /home/some/path/exception_hooks.py line 22, in <module> # ValueError, Message: Some error message
The above example provides part of the exception information, but in order to obtain the required information for debugging the code All information, and know the time and location of the exception, we also need to study the traceback object in depth:
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'}
As can be seen from the above example, the traceback object (tb) is essentially a linked list - storing all Exceptions occur. Therefore, you can use tb_next to traverse tb and print each exception information. On this basis, you can also use the tb_frame.f_locals attribute to output variables to the console, which helps debugging the code.
It is feasible to use the traceback object to output exception information, but it is more troublesome. In addition, the output information is less readable. A more convenient approach is to use the traceback module, which has many built-in auxiliary functions for extracting exception information.
We have now introduced the basic knowledge of Exception Hooks. Next, we can customize an exception hooks and add some practical features.
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")
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
# 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}")
In addition to the examples introduced above, you can also output the local variables of each frame, or find the variables referenced in the line where the exception occurs. These Exception Hooks are already very mature. Compared to custom Exception hooks, I suggest you read the source code of other developers and learn their design ideas.
It is interesting to customize an Exception Hook, but many third-party libraries have already implemented this function. Rather than reinventing the wheel, look at other great tools.
# 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
# https://github.com/Qix-/better-exceptions # pip install better_exceptions # export BETTER_EXCEPTIONS=1 import better_exceptions better_exceptions.MAX_LENGTH = None # 检查你的 TERM 变量是否被设置为 `xterm`, 如果没有执行以下操作 # See issue: https://github.com/Qix-/better-exceptions/issues/8 better_exceptions.SUPPORTS_COLOR = True better_exceptions.hook() do_stuff()# Raises ValueError
# https://github.com/onelivesleft/PrettyErrors/ # pip install pretty_errors import pretty_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()
In addition to direct import , the code above also shows some optional configurations of the library. More configurations can be viewed here: Configuration [3]
# 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()
# https://github.com/cknd/stackprinter # pip install stackprinter import stackprinter stackprinter.set_excepthook(style='darkbg2') do_stuff()
本文我们学习了如何自定义Exception Hooks,但我更推荐使用第三方库。你可以在本文介绍的第三方库中任选一个喜欢的,用到项目中。需要注意的是使用自定义Exception Hooks可能会丢失某些关键信息,例如:本文中的某些例子中,输出中缺少文件路径,在远程调试代码这无疑很不方便,因此,需要谨慎使用。
The above is the detailed content of Increase your knowledge! Python's exception information can also be displayed like this. For more information, please follow other related articles on the PHP Chinese website!