如何调试导致python后续例外的堆栈跟踪?

问题描述 投票:0回答:5
Python(和Ipython)具有非常强大的后验证功能,可以在追溯中的每个范围内进行可变检查和命令执行。上/下调试器命令允许更改最终异常的堆栈跟踪的帧,但是该异常的

__cause__

(由
raise ... from ...
语法定义)呢?
Python 3.7.6 (default, Jan 8 2020, 13:42:34) Type 'copyright', 'credits' or 'license' for more information IPython 7.11.1 -- An enhanced Interactive Python. Type '?' for help. In [1]: def foo(): ...: bab = 42 ...: raise TypeError ...: In [2]: try: ...: foo() ...: except TypeError as err: ...: barz = 5 ...: raise ValueError from err ...: --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-2-dd046d7cece0> in <module> 1 try: ----> 2 foo() 3 except TypeError as err: <ipython-input-1-da9a05838c59> in foo() 2 bab = 42 ----> 3 raise TypeError 4 TypeError: The above exception was the direct cause of the following exception: ValueError Traceback (most recent call last) <ipython-input-2-dd046d7cece0> in <module> 3 except TypeError as err: 4 barz = 5 ----> 5 raise ValueError from err 6 ValueError: In [3]: %debug > <ipython-input-2-dd046d7cece0>(5)<module>() 2 foo() 3 except TypeError as err: 4 barz = 5 ----> 5 raise ValueError from err 6 ipdb> barz 5 ipdb> bab *** NameError: name 'bab' is not defined ipdb> down *** Newest frame ipdb> up *** Oldest frame

有一种方法可以从调试器访问
bab

Edit:我意识到验尸后不仅仅是Ipython和IPDB的功能,它实际上是Vanilla PDB的一部分。以上也可以通过将代码放入脚本
testerr.py

并运行

python -m pdb testerr.py

并运行
continue
来复制。错误后,它说
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program

在同一地点进行调试器。
	

您可以使用

python python-3.x ipython pdb ipdb
5个回答
8
投票
方法

保留原始异常的追溯:

try: 
    foo()
except TypeError as err:
    barz = 5
    raise ValueError().with_traceback(err.__traceback__) from err
注意,我已经更新了代码以提出异常实例而不是异常类别。

iPython
中的完整代码段:

In [1]: def foo(): ...: bab = 42 ...: raise TypeError() ...: In [2]: try: ...: foo() ...: except TypeError as err: ...: barz = 5 ...: raise ValueError().with_traceback(err.__traceback__) from err ...: --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-2-a5a6d81e4c1a> in <module> 1 try: ----> 2 foo() 3 except TypeError as err: <ipython-input-1-ca1efd1bee60> in foo() 2 bab = 42 ----> 3 raise TypeError() 4 TypeError: The above exception was the direct cause of the following exception: ValueError Traceback (most recent call last) <ipython-input-2-a5a6d81e4c1a> in <module> 3 except TypeError as err: 4 barz = 5 ----> 5 raise ValueError().with_traceback(err.__traceback__) from err 6 <ipython-input-2-a5a6d81e4c1a> in <module> 1 try: ----> 2 foo() 3 except TypeError as err: 4 barz = 5 5 raise ValueError().with_traceback(err.__traceback__) from err <ipython-input-1-ca1efd1bee60> in foo() 1 def foo(): 2 bab = 42 ----> 3 raise TypeError() 4 ValueError: In [3]: %debug > <ipython-input-1-ca1efd1bee60>(3)foo() 1 def foo(): 2 bab = 42 ----> 3 raise TypeError() 4 ipdb> bab 42 ipdb> u > <ipython-input-2-a5a6d81e4c1a>(2)<module>() 1 try: ----> 2 foo() 3 except TypeError as err: 4 barz = 5 5 raise ValueError().with_traceback(err.__traceback__) from err ipdb> u > <ipython-input-2-a5a6d81e4c1a>(5)<module>() 2 foo() 3 except TypeError as err: 4 barz = 5 ----> 5 raise ValueError().with_traceback(err.__traceback__) from err 6 ipdb> barz 5

Edit-一种替代的下等方法 addressing @user2357112SupportsMonica的

firstcomment
,如果您希望避免在日志中避免原始异常的追踪的多个转储,则可以
raise from None

。但是,正如 @user2357112supportsmonica的

秒评论指出,这隐藏了原始异常的消息。在常见的情况下,这尤其有问题,而您却不是在验尸调试而是检查印刷的追溯。

try: foo() except TypeError as err: barz = 5 raise ValueError().with_traceback(err.__traceback__) from None

Ipython
中的代码片段: In [4]: try: ...: foo() ...: except TypeError as err: ...: barz = 5 ...: raise ValueError().with_traceback(err.__traceback__) from None ...: --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-6-b090fb9c510e> in <module> 3 except TypeError as err: 4 barz = 5 ----> 5 raise ValueError().with_traceback(err.__traceback__) from None 6 <ipython-input-6-b090fb9c510e> in <module> 1 try: ----> 2 foo() 3 except TypeError as err: 4 barz = 5 5 raise ValueError().with_traceback(err.__traceback__) from None <ipython-input-2-ca1efd1bee60> in foo() 1 def foo(): 2 bab = 42 ----> 3 raise TypeError() 4 ValueError: In [5]: %debug > <ipython-input-2-ca1efd1bee60>(3)foo() 1 def foo(): 2 bab = 42 ----> 3 raise TypeError() 4 ipdb> bab 42 ipdb> u > <ipython-input-6-b090fb9c510e>(2)<module>() 1 try: ----> 2 foo() 3 except TypeError as err: 4 barz = 5 5 raise ValueError().with_traceback(err.__traceback__) from None ipdb> u > <ipython-input-6-b090fb9c510e>(5)<module>() 3 except TypeError as err: 4 barz = 5 ----> 5 raise ValueError().with_traceback(err.__traceback__) from None 6 ipdb> barz 5

需要priansing
from None
,因为否则将进行链接

implicry,将原始异常作为新的例外属性。请注意,这与明确完成链接时设置的__context__

属性不同。
__cause__

yoel答案有效,应该是您的首选过程,但是如果跟踪更难调试,则可以使用In [6]: try: 
   ...:     foo() 
   ...: except TypeError as err: 
   ...:     barz = 5 
   ...:     raise ValueError().with_traceback(err.__traceback__) 
   ...:                                                                                                                                                         
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-ee78991171cb> in <module>
      1 try:
----> 2     foo()
      3 except TypeError as err:

<ipython-input-2-ca1efd1bee60> in foo()
      2     bab = 42
----> 3     raise TypeError()
      4 

TypeError: 

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-5-ee78991171cb> in <module>
      3 except TypeError as err:
      4     barz = 5
----> 5     raise ValueError().with_traceback(err.__traceback__)
      6 

<ipython-input-5-ee78991171cb> in <module>
      1 try:
----> 2     foo()
      3 except TypeError as err:
      4     barz = 5
      5     raise ValueError().with_traceback(err.__traceback__)

<ipython-input-2-ca1efd1bee60> in foo()
      1 def foo():
      2     bab = 42
----> 3     raise TypeError()
      4 

ValueError: 
模块。
跟踪模块将打印出执行的每个指令,按行线。不过,有一个渔获。标准库和软件包调用也将被追踪,这可能意味着该跟踪将充斥着没有意义的代码。
要避免这种行为,您可以通过python库和站点包装文件夹的位置传递
trace
参数。

run
--ignore-dir
要找到网站包的位置,然后以以下参数调用跟踪:

2
投票
python -m site

用所有文件夹重新置换
python -m trace --trace --ignore-dir=/usr/lib/python3.8:/usr/local/lib/python3.8/dist-packages main.py args

,并使用脚本位置和参数。

如果要运行某个功能,您也可以直接在代码中使用跟踪模块,请参阅此示例从

Https://docs.python.org/3.0/library/trace.html

中提取的示例。
ignore-dir

i我还找到了一种方法来执行此操作,而无需修改基础源代码 - 简单地在Mortem Debugger中运行命令。

i从this Answer那里看到了您可以直接从追溯实例获得当地人。
main.py args

由于后者使用后者,您可以简单地沿着一系列例外移动,然后通过覆盖所需的级别进行调试。  从
import sys
import trace

# create a Trace object, telling it what to ignore, and whether to
# do tracing or line-counting or both.
tracer = trace.Trace(
    ignoredirs=[sys.prefix, sys.exec_prefix],
    trace=0,
    count=1)

# run the new command using the given tracer
tracer.run('main()')

# make a report, placing output in /tmp
r = tracer.results()
r.write_results(show_missing=True, coverdir="/tmp")
开始,将

(Pdb) ll 1 -> def foo(): 2 bab = 42 3 raise TypeError 4 5 try: 6 foo() 7 except TypeError as err: 8 barz = 5 9 >> raise ValueError from err 10 (Pdb) err # not sure why err is not defined *** NameError: name 'err' is not defined (Pdb) import sys (Pdb) sys.exc_info() (<class 'AttributeError'>, AttributeError("'Pdb' object has no attribute 'do_sys'"), <traceback object at 0x107cb5be0>) (Pdb) err = sys.exc_info()[1].__context__ (Pdb) err # here we go ValueError() (Pdb) err.__cause__ TypeError() (Pdb) err.__traceback__.tb_next.tb_next.tb_next.tb_next.tb_frame.f_locals['barz'] 5 (Pdb) err.__cause__.__traceback__.tb_next.tb_frame.f_locals['bab'] 42

链沿所需的水平(sys.last_value.__traceback__ is sys.last_traceback)走,然后设置

ipdb
i写了一个小小的ipython魔术,

2
投票

更多teetailed

堆栈跟踪,包括每个级别的所有local变量。 首先,使用sys.last_value命令安装此库,然后在您的代码中放一行:

val.__context__

1
投票

new

未启用堆栈快照时:
sys.last_traceback = new.__traceback__
启用堆栈快照后,更容易,更方便地确定问题,同时知道
%debug
~/.ipython/profile_default/startup
的值:
%chain -h
也可以手动输出外观:

pip install stack-snapshot 仅通过修改

import stack_snapshot; stack_snapshot.init()
方法,例如
import stack_snapshot

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

stack_snapshot.init()

x = 1; y = 0
print(inner(x, y))
, 或将函数调用重定向到异常类,例如

Traceback (most recent call last): File "PyStackSnapshot\test.py", line 9, in <module> print(inner(x,y)) File "PyStackSnapshot\test.py", line 4, in inner if y == 0: raise ZeroDivisionError ZeroDivisionError

1
投票
在附加方面,可以将跟踪的变量信息粘贴到wenerativeai

工具中,包括chatgpt,使它们生成更精确的代码。 在这里可以看到更多详细的用法及其工作方式:Pystacksnapshot·github,我是x库的开发人员。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.