traceback模块

发布时间 2023-10-15 22:16:23作者: lxd670

终端报错输出

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)

if __name__ == '__main__':
    f3()
python3 error_test.py
Traceback (most recent call last):
  File "/Users/code/python_code/error_test.py", line 12, in <module>
    f3()
  File "/Users/code/python_code/error_test.py", line 8, in f3
    res = f1(2, 0)
  File "/Users/code/python_code/error_test.py", line 2, in f1
    return f2(x, y)
  File "/Users/code/python_code/error_test.py", line 5, in f2
    return x / y
ZeroDivisionError: division by zero

使用异常捕获输出

import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)

if __name__ == '__main__':
    try:
        f3()
    except Exception as e:
        print(f"{type(e)}, {e}\n")
<class 'ZeroDivisionError'>, division by zero

使用sys.exc_info显示输出

案例

import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)

if __name__ == '__main__':
    try:
        f3()
    except Exception:
        exc_type, exc_value, exc_traceback_obj = sys.exc_info()
        print("type ==> %s" % exc_type)
        print("value ==> %s type: %s\n" %(exc_value, type(exc_value)))
        print("traceback ==> exc_traceback_obj: %s" % exc_traceback_obj)
        print("traceback ==> file name: %s" %(exc_traceback_obj.tb_frame.f_code.co_filename))
        print("traceback ==> line no: %s" %(exc_traceback_obj.tb_lineno))
        print("traceback ==> function name: %s" %(exc_traceback_obj.tb_frame.f_code.co_name))
type ==> <class 'ZeroDivisionError'>
value ==> division by zero type: <class 'ZeroDivisionError'>

traceback ==> exc_traceback_obj: <traceback object at 0x10296dd40>
traceback ==> file name: /Users/code/python_code/error_test.py
traceback ==> line no: 16
traceback ==> function name: <module>

使用traceback

主要用于打印输出或者保存到文件中

traceback.print_tb

这个函数将堆栈跟踪信息打印到指定文件对象中,可以用于调试时输出详细的错误信息。

traceback.print_tb(tb, limit=None, file=None)

  • tb: 表示堆栈跟踪的对象。
  • limit: 控制显示的堆栈帧数量。
  • file: 输出的文件对象,默认是 sys.stderr

源码分析

def print_tb(tb, limit=None, file=None)
	print_list(extract_tb(tb, limit=limit), file=file)

def print_list(extracted_list, file=None):
  if file is None:
      file = sys.stderr
  for item in StackSummary.from_list(extracted_list).format():
      print(item, file=file, end="")

什么是tb对象

tb是traceback对象

异常对象中获取
import traceback
import sys
def func1():
    func2()

def func2():
    func3()

def func3():
    return 1 / 0

# 调用函数
try:
    func1()
except Exception as e:
    print(type(e.__traceback__))
    print(e.__traceback__)
<class 'traceback'>
<traceback object at 0x100ac18c0>
sys.exc_info中获取
import traceback
import sys
def func1():
    func2()

def func2():
    func3()

def func3():
    return 1 / 0

# 调用函数
try:
    func1()
except Exception:
    _,_, tb = sys.exc_info()
    print(type(tb))
    print(tb)
<class 'traceback'>
<traceback object at 0x1052598c0>

案例一:更具获取tb对象不同

获取异常对象的 __traceback__
import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except ZeroDivisionError as e:
    traceback.print_tb(tb=e.__traceback__)
File "/Users/code/python_code/error_test.py", line 14, in <module>
  f3()
File "/Users/code/python_code/error_test.py", line 11, in f3
  res = f1(2, 0)
File "/Users/code/python_code/error_test.py", line 5, in f1
  return f2(x, y)
File "/Users/code/python_code/error_test.py", line 8, in f2
  return x / y
获取sys.exc_info()的traceback对象

推荐使用sys.exc_info()输出信息

import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except ZeroDivisionError as e:
    exc_type, exc_value, exc_traceback_obj = sys.exc_info()
    traceback.print_tb(tb=exc_traceback_obj)
File "/Users/code/python_code/error_test.py", line 14, in <module>
  f3()
File "/Users/code/python_code/error_test.py", line 11, in f3
  res = f1(2, 0)
File "/Users/code/python_code/error_test.py", line 5, in f1
  return f2(x, y)
File "/Users/code/python_code/error_test.py", line 8, in f2
  return x / y
使用 sys.exc_info() 和使用异常对象的 __traceback__ 属性区别

从使用的角度来看,通常推荐直接使用异常对象的 __traceback__ 属性,因为它更直观,并且适用于当前异常的上下文。如果你需要获取当前线程的全局异常信息,那么可以使用 sys.exc_info()

  • 来源对象不同:
    • sys.exc_info() 返回一个包含异常信息的元组 (exc_type, exc_value, exc_traceback),其中 exc_traceback 是 traceback 对象。
    • 异常对象的 __traceback__ 直接提供了 traceback 对象。
  • 使用场景不同:
    • sys.exc_info() 可以在任何地方获取当前线程中的异常信息。它返回全局状态,适用于多线程环境。
    • 异常对象的 __traceback__ 属性则直接从异常对象中获取,适用于当前异常对象的上下文。

案例二:使用limit

设置limit=1后,发现值查找深度为1层

import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except ZeroDivisionError as e:
    traceback.print_tb(tb=e.__traceback__, limit=1)
File "/Users/code/python_code/error_test.py", line 14, in <module>
  f3()

案例三:使用file

import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except ZeroDivisionError as e:
    with open("traceback_output.txt", "w") as file:
        traceback.print_tb(e.__traceback__, limit=None, file=file)
  File "/Users/code/python_code/error_test.py", line 14, in <module>
    f3()
  File "/Users/code/python_code/error_test.py", line 11, in f3
    res = f1(2, 0)
  File "/Users/code/python_code/error_test.py", line 5, in f1
    return f2(x, y)
  File "/Users/code/python_code/error_test.py", line 8, in f2
    return x / y

traceback.print_exception

这个函数将异常信息和堆栈跟踪信息打印到指定文件对象中,允许你输出完整的异常链信息。

traceback.print_exception(etype, value, tb, limit=None, file=None, chain=True)

  • etype (type): 异常类型。
  • value (Exception对象): 异常实例。
  • tb (traceback对象): 堆栈跟踪对象。
  • limit (int, 可选): 控制打印的堆栈帧数量。
  • file (文件对象, 可选): 输出的文件对象,默认是 sys.stderr
  • chain (bool, 可选): 如果为 False,则不会打印异常链的上一级异常。

源码

def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
                    file=None, chain=True):
  value, tb = _parse_value_tb(exc, value, tb)
  if file is None:
      file = sys.stderr
  te = TracebackException(type(value), value, tb, limit=limit, compact=True)
  for line in te.format(chain=chain):
      print(line, file=file, end="")

案例

import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except ZeroDivisionError as e:
  	# 也可以使用一下案例,输出都是一样的
  	# traceback.print_exception(type(e), e, e.__traceback__)
    traceback.print_exception(*sys.exc_info())
Traceback (most recent call last):
  File "/Users/code/python_code/error_test.py", line 14, in <module>
    f3()
  File "/Users/code/python_code/error_test.py", line 11, in f3
    res = f1(2, 0)
  File "/Users/code/python_code/error_test.py", line 5, in f1
    return f2(x, y)
  File "/Users/code/python_code/error_test.py", line 8, in f2
    return x / y
ZeroDivisionError: division by zero

traceback.print_exc

traceback.print_exc(limit=None, file=None, chain=True)

  • limit (int, 可选): 控制打印的堆栈帧数量。
  • file (文件对象, 可选): 输出的文件对象,默认是 sys.stderr
  • chain (bool, 可选): 如果为 False,则不会打印异常链的上一级异常。

源码

print_exception这是 print_exception(*sys.exc_info(), limit, file, chain) 的简写形式

def print_exc(limit=None, file=None, chain=True):
    print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain)

默认chain=True

import sys
import traceback

def f1(x, y):
    try:
        return f2(x, y)
    except ZeroDivisionError as e:
        raise ValueError("Custom error")
    
def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except ValueError as e:
    traceback.print_exc(chain=True)
Traceback (most recent call last):
  File "/Users/code/python_code/error_test.py", line 6, in f1
    return f2(x, y)
  File "/Users/code/python_code/error_test.py", line 11, in f2
    return x / y
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/code/python_code/error_test.py", line 17, in <module>
    f3()
  File "/Users/code/python_code/error_test.py", line 14, in f3
    res = f1(2, 0)
  File "/Users/code/python_code/error_test.py", line 8, in f1
    raise ValueError("Custom error")
ValueError: Custom error

设置chain=False

import sys
import traceback

def f1(x, y):
    try:
        return f2(x, y)
    except ZeroDivisionError as e:
        raise ValueError("Custom error")
    
def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except ValueError as e:
    traceback.print_exc(chain=False)
Traceback (most recent call last):
  File "/Users/code/python_code/error_test.py", line 17, in <module>
    f3()
  File "/Users/code/python_code/error_test.py", line 14, in f3
    res = f1(2, 0)
  File "/Users/code/python_code/error_test.py", line 8, in f1
    raise ValueError("Custom error")
ValueError: Custom error

traceback.print_stack

函数用于打印当前堆栈的信息,显示调用函数的序列。这对于调试和了解程序执行路径非常有用。

traceback.print_stack(f=None, limit=None, file=None)

  • f: 参数是一个可选的帧对象(frame对象并不是一个FrameSummary对象),用于指定提取信息的起点。

  • limit (int, 可选): 控制打印的堆栈帧数量。

  • file (文件对象, 可选): 输出的文件对象,默认是 sys.stderr

源码

def print_stack(f=None, limit=None, file=None):
    if f is None:
        f = sys._getframe().f_back
    print_list(extract_stack(f, limit=limit), file=file)

案例

import traceback

def example_function():
    traceback.print_stack()

def another_function():
    example_function()

another_function()
File "/Users/code/python_code/error_test.py", line 9, in <module>
  another_function()
File "/Users/code/python_code/error_test.py", line 7, in another_function
  example_function()
File "/Users/code/python_code/error_test.py", line 4, in example_function
  traceback.print_stack()

format_xx方法

把print的输出内容转化为字符串列表形似返回

traceback.format_tb

这个函数返回一个字符串列表,其中包含有关堆栈跟踪的信息。通常用于将信息记录到文件或以其他形式存储和处理。

traceback.format_tb(tb, limit=None)

  • tb :表示堆栈跟踪的对象。
  • limit : 控制显示的堆栈帧数量。如果为 None,则显示所有帧。

源码

def format_tb(tb, limit=None):
  	# extract_tb创建了StackSummary.extract(walk_tb(tb), limit=limit)对象,并使用format方法进行了处理
    return extract_tb(tb, limit=limit).format()

limit为None

import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except ZeroDivisionError as e:
    exc_type, exc_value, exc_traceback_obj = sys.exc_info()
    tb_list = traceback.format_tb(tb=exc_traceback_obj)
    print(f"tb_list: {tb_list}")
    print(">>>>>Traceback:")
    print("".join(tb_list))
    print(">>>>>Error:", e)
tb_list: ['  File "/Users/code/python_code/error_test.py", line 14, in <module>\n    f3()\n', '  File "/Users/code/python_code/error_test.py", line 11, in f3\n    res = f1(2, 0)\n', '  File "/Users/code/python_code/error_test.py", line 5, in f1\n    return f2(x, y)\n', '  File "/Users/code/python_code/error_test.py", line 8, in f2\n    return x / y\n']
>>>>>Traceback:
  File "/Users/code/python_code/error_test.py", line 14, in <module>
    f3()
  File "/Users/code/python_code/error_test.py", line 11, in f3
    res = f1(2, 0)
  File "/Users/code/python_code/error_test.py", line 5, in f1
    return f2(x, y)
  File "/Users/code/python_code/error_test.py", line 8, in f2
    return x / y

>>>>>Error: division by zero

设置limit值

设置limit=2后,发现值查找深度为2层

import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except ZeroDivisionError as e:
    exc_type, exc_value, exc_traceback_obj = sys.exc_info()
    tb_list = traceback.format_tb(tb=exc_traceback_obj, limit=2)
    print(">>>>>Traceback:")
    print("".join(tb_list))
    print(">>>>>Error:", e)
>>>>>Traceback:
  File "/Users/code/python_code/error_test.py", line 14, in <module>
    f3()
  File "/Users/code/python_code/error_test.py", line 11, in f3
    res = f1(2, 0)

>>>>>Error: division by zero

traceback.format_exception

traceback.format_exception(etype, value, tb, limit=None, chain=True)

返回一个字符串列表,表示堆栈跟踪信息。

  • etype: 异常类型。
  • value: 异常实例。
  • tb: 表示堆栈跟踪的对象。
  • limit: 控制显示的堆栈帧数量。
  • chain: 如果为 False,则不会显示异常链的上一级异常

源码

def format_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
                     chain=True):
    value, tb = _parse_value_tb(exc, value, tb)
    te = TracebackException(type(value), value, tb, limit=limit, compact=True)
    return list(te.format(chain=chain))

案例

import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except Exception:
    res = traceback.format_exception(*sys.exc_info())
    print(type(res))
    print(res)
<class 'list'>
['Traceback (most recent call last):\n', '  File "/Users/code/python_code/error_test.py", line 14, in <module>\n    f3()\n', '  File "/Users/code/python_code/error_test.py", line 11, in f3\n    res = f1(2, 0)\n', '  File "/Users/code/python_code/error_test.py", line 5, in f1\n    return f2(x, y)\n', '  File "/Users/code/python_code/error_test.py", line 8, in f2\n    return x / y\n', 'ZeroDivisionError: division by zero\n']

traceback.format_exc

traceback.format_exctraceback.format_exception的简写形式,返回一个字符串

"".join(format_exception(*sys.exc_info(), limit=limit, chain=chain))

format_exc(limit=None, chain=True)

  • limit: 控制显示的堆栈帧数量。
  • chain: 如果为 False,则不会显示异常链的上一级异常。
import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except Exception:
    res = traceback.format_exc()
    print(type(res))
    print(res)
<class 'str'>
Traceback (most recent call last):
  File "/Users/code/python_code/error_test.py", line 14, in <module>
    f3()
  File "/Users/code/python_code/error_test.py", line 11, in f3
    res = f1(2, 0)
  File "/Users/code/python_code/error_test.py", line 5, in f1
    return f2(x, y)
  File "/Users/code/python_code/error_test.py", line 8, in f2
    return x / y
ZeroDivisionError: division by zero

traceback.format_stack

format_stack(f=None, limit=None)

  • f : 参数是一个可选的帧对象(frame对象并不是一个FrameSummary对象),用于指定提取信息的起点。
  • limit: 控制显示的堆栈帧数量。

源码

def format_list(extracted_list):
    return StackSummary.from_list(extracted_list).format()

def format_stack(f=None, limit=None):
  	# 调用了extract_stack,并使用format_list进行格式化
    if f is None:
        f = sys._getframe().f_back
    return format_list(extract_stack(f, limit=limit))

案例

import traceback

def get_format_stack_info():
    res = traceback.format_stack()
    print(res)

get_format_stack_info()
['  File "/Users/code/python_code/error_test.py", line 7, in <module>\n    get_format_stack_info()\n', '  File "/Users/code/python_code/error_test.py", line 4, in get_format_stack_info\n    res = traceback.format_stack()\n']

extract_xx方法

比print_xx和format_xx更深入的方法

traceback.extract_tb

返回一个由 traceback.FrameSummary 对象组成的列表,每个对象表示一个堆栈帧

traceback.extract_tb(tb, limit=None)

  • tb: 表示堆栈跟踪的对象。
  • limit: 控制显示的堆栈帧数量。

源码

def extract_tb(tb, limit=None):
    return StackSummary.extract(walk_tb(tb), limit=limit)
    
def walk_tb(tb):
    while tb is not None:
        yield tb.tb_frame, tb.tb_lineno
        tb = tb.tb_next

案例

import sys
import traceback

def f1(x, y):
    return f2(x, y)
    
def f2(x, y):
    return x / y

def f3():
    res = f1(2, 0)
    print(res)
try:
    f3()
except ZeroDivisionError as e:
    exc_type, exc_value, exc_traceback_obj = sys.exc_info()
    # [traceback.FrameSummary, traceback.FrameSummary, ...]
    traceback_framesummary_list = traceback.extract_tb(exc_traceback_obj)
    for traceback_framesummary in traceback_framesummary_list:
        """
        traceback_framesummary是traceback.FrameSummary对象
        filename: 文件名
        name: 方法名
        lineno: 报错文件行号
        line: 报错行对应的code
        locals: 一个字典或者None
        """
        print(f"-------{type(traceback_framesummary)}-------")
        print(traceback_framesummary.filename, traceback_framesummary.name, traceback_framesummary.lineno)
        print(traceback_framesummary.line)
        print(traceback_framesummary.locals)
-------<class 'traceback.FrameSummary'>-------
/Users/code/python_code/error_test.py <module> 14
f3()
None
-------<class 'traceback.FrameSummary'>-------
/Users/code/python_code/error_test.py f3 11
res = f1(2, 0)
None
-------<class 'traceback.FrameSummary'>-------
/Users/code/python_code/error_test.py f1 5
return f2(x, y)
None
-------<class 'traceback.FrameSummary'>-------
/Users/code/python_code/error_test.py f2 8
return x / y
None

traceback.extract_stack

traceback.extract_stack(f=None, limit=None)

从当前调用栈中提取信息,返回一个由 FrameSummary 对象组成的列表。

  • f 参数是一个可选的帧对象(frame对象并不是一个FrameSummary对象),用于指定提取信息的起点。
  • limit 参数用于控制显示的堆栈帧数量。

源码

def extract_stack(f=None, limit=None):
    if f is None:
        f = sys._getframe().f_back
    stack = StackSummary.extract(walk_stack(f), limit=limit)
    stack.reverse()
    return stack

def walk_stack(f):
    if f is None:
        f = sys._getframe().f_back.f_back
    while f is not None:
        yield f, f.f_lineno
        f = f.f_back

调用信息

import traceback

def get_extract_stack_info():
    stack_info = traceback.extract_stack()
    for frame in stack_info:
        print(frame)

get_extract_stack_info()
<FrameSummary file /Users/code/python_code/error_test.py, line 8 in <module>>
<FrameSummary file /Users/code/python_code/error_test.py, line 4 in get_extract_stack_info>

多层调用栈信息

import sys
import traceback

def f1(x, y):
    return f2(x, y)

def f2(x, y):
    stack_info = traceback.extract_stack()
    print("extract_stack information:")
    for frame in stack_info:
        print(frame)
    return x + y

def f3():
    res = f1(2, 0)
    print(res)

f3()
extract_stack information:
<FrameSummary file /Users/code/python_code/error_test.py, line 18 in <module>>
<FrameSummary file /Users/code/python_code/error_test.py, line 15 in f3>
<FrameSummary file /Users/code/python_code/error_test.py, line 5 in f1>
<FrameSummary file /Users/code/python_code/error_test.py, line 8 in f2>
2

extract_stack和inspect.stack区别

traceback.extract_stack():

  • 返回类型:返回一个列表,其中的每个元素是 FrameSummary 对象。
  • 提供的信息:主要包含了文件名、行号、函数名等基本信息,不提供局部变量等详细信息。
  • 轻量级:由于提供的信息较为简略,extract_stack() 在某些情况下可能会更轻量。
  • 用法:适用于只关注调用栈的基本结构,例如文件名、行号等。

inspect.stack():

  • 返回类型:返回一个列表,其中的每个元素是 FrameInfo 对象。
  • 提供的信息:包含了更详细的信息,如局部变量、全局变量等。FrameInfo 对象比 FrameSummary 提供更多的上下文信息。
  • 更全面:提供更全面的堆栈信息,适用于需要深入了解调用栈上下文的情况。
  • 用法:适用于需要更详细的调用栈信息,比如查看局部变量的值等。
import traceback
import inspect

def get_extract_stack_info():
    stack_info = traceback.extract_stack()
    print("extract_stack information:")
    for frame in stack_info:
        print(frame)

def get_inspect_info():
    print("\nInspect information:")
    inspect_info = inspect.stack()
    for frame, _, _, _, _, _ in inspect_info:
        print(frame)

get_extract_stack_info()

get_inspect_info()
extract_stack information:
<FrameSummary file /Users/code/python_code/error_test.py, line 16 in <module>>
<FrameSummary file /Users/code/python_code/error_test.py, line 5 in get_extract_stack_info>

Inspect information:
<frame at 0x1032c19a0, file '/Users/code/python_code/error_test.py', line 14, code get_inspect_info>
<frame at 0x1030d1a40, file '/Users/code/python_code/error_test.py', line 18, code <module>>