如何将python回溯限制为特定文件

J-b*_*bob 29 python debugging traceback

我写了很多使用外部库的Python代码.我经常写一个bug,当我运行代码时,我会在Python控制台中获得一个很长的回溯.99.999999%的时间是由于我的代码中的编码错误,而不是因为包中的错误.但是回溯一直到程序包代码中的错误行,要么需要大量滚动回溯来查找我编写的代码,要么回溯是如此深入到我自己的代码所没有的包中.甚至出现在追溯中.

有没有办法"黑盒子"包装代码,或以某种方式只显示我的代码中的追溯线?我希望能够向系统指定我想要追溯的目录或文件.

dno*_*zay 15

为了打印自己的堆栈跟踪,您需要自己处理所有未处理的异常; 这就是sys.excepthook方便的方式.

该功能的签名是sys.excepthook(type, value, traceback),它的工作是:

此函数打印出给定的回溯和异常sys.stderr.

因此,只要您可以使用追溯并且仅提取您关心的部分,您应该没问题.测试框架经常这样做; 它们具有assert通常不会出现在回溯中的自定义函数,换句话说,它们会跳过属于测试框架的框架.此外,在这些情况下,测试通常也由测试框架启动.

最终得到一个如下所示的回溯:

[ custom assert code ]+ ... [ code under test ] ...+[ test runner code ]

如何识别您的代码.

您可以在代码中添加全局:

__mycode = True
Run Code Online (Sandbox Code Playgroud)

然后识别帧:

def is_mycode(tb):
  globals = tb.tb_frame.f_globals
  return globals.has_key('__mycode')
Run Code Online (Sandbox Code Playgroud)

如何提取你的帧.

  1. 跳过与您无关的帧(例如自定义断言代码)
  2. 确定代码中有多少帧 - > length
  3. 提取length

    def mycode_traceback_levels(tb):
      length = 0
      while tb and is_mycode(tb):
        tb = tb.tb_next
        length += 1
      return length
    
    Run Code Online (Sandbox Code Playgroud)

示例处理程序

def handle_exception(type, value, tb):
  # 1. skip custom assert code, e.g.
  # while tb and is_custom_assert_code(tb):
  #   tb = tb.tb_next
  # 2. only display your code
  length = mycode_traceback_levels(tb)
  print ''.join(traceback.format_exception(type, value, tb, length))
Run Code Online (Sandbox Code Playgroud)

安装处理程序:

sys.excepthook = handle_exception
Run Code Online (Sandbox Code Playgroud)

接下来是什么?

length如果您仍需要有关故障在您自己的代码之外的位置的信息,则可以调整以添加一个或多个级别.

另见https://gist.github.com/dnozay/b599a96dc2d8c69b84c6


vau*_*tah 6

正如其他人建议的那样,您可以使用sys.excepthook

此函数将给定的回溯和异常打印到sys.stderr

当异常被引发并未被捕获时,解释使用三个参数调用sys.excepthook,异常类、异常实例和回溯对象。在交互式会话中,这发生在控制返回到提示之前;在 Python 程序中,这发生在程序退出之前。可以通过将另一个三参数函数分配给sys.excepthook 来自定义此类顶级异常的处理

(强调我的)

可以根据指定的目录过滤由extract_tb(或从traceback模块中的类似函数)提取的回溯。

可以提供帮助的两个功能:

from os.path import join, abspath
from traceback import extract_tb, format_list, format_exception_only

def spotlight(*show):
    ''' Return a function to be set as new sys.excepthook.
        It will SHOW traceback entries for files from these directories. '''
    show = tuple(join(abspath(p), '') for p in show)

    def _check_file(name):
        return name and name.startswith(show)

    def _print(type, value, tb):
        show = (fs for fs in extract_tb(tb) if _check_file(fs.filename))
        fmt = format_list(show) + format_exception_only(type, value)
        print(''.join(fmt), end='', file=sys.stderr)

    return _print

def shadow(*hide):
    ''' Return a function to be set as new sys.excepthook.
        It will HIDE traceback entries for files from these directories. '''
    hide = tuple(join(abspath(p), '') for p in hide)

    def _check_file(name):
        return name and not name.startswith(hide)

    def _print(type, value, tb):
        show = (fs for fs in extract_tb(tb) if _check_file(fs.filename))
        fmt = format_list(show) + format_exception_only(type, value)
        print(''.join(fmt), end='', file=sys.stderr)

    return _print
Run Code Online (Sandbox Code Playgroud)

他们都使用traceback.extract_tb. 它返回“从回溯对象中提取的“预处理”堆栈跟踪条目列表”;它们都是traceback.FrameSummary(命名元组)的实例。每个traceback.FrameSummary对象都有一个filename字段,用于存储相应文件的绝对路径。我们检查它是否以作为单独函数参数提供的任何目录路径开头,以确定我们是否需要排除该条目(或保留它)。


这是一个例子

enum标准库中的模块不允许重复使用密钥,

import enum
enum.Enum('Faulty', 'a a', module=__name__)
Run Code Online (Sandbox Code Playgroud)

产量

from os.path import join, abspath
from traceback import extract_tb, format_list, format_exception_only

def spotlight(*show):
    ''' Return a function to be set as new sys.excepthook.
        It will SHOW traceback entries for files from these directories. '''
    show = tuple(join(abspath(p), '') for p in show)

    def _check_file(name):
        return name and name.startswith(show)

    def _print(type, value, tb):
        show = (fs for fs in extract_tb(tb) if _check_file(fs.filename))
        fmt = format_list(show) + format_exception_only(type, value)
        print(''.join(fmt), end='', file=sys.stderr)

    return _print

def shadow(*hide):
    ''' Return a function to be set as new sys.excepthook.
        It will HIDE traceback entries for files from these directories. '''
    hide = tuple(join(abspath(p), '') for p in hide)

    def _check_file(name):
        return name and not name.startswith(hide)

    def _print(type, value, tb):
        show = (fs for fs in extract_tb(tb) if _check_file(fs.filename))
        fmt = format_list(show) + format_exception_only(type, value)
        print(''.join(fmt), end='', file=sys.stderr)

    return _print
Run Code Online (Sandbox Code Playgroud)

我们可以将堆栈跟踪条目限制在我们的代码中(在/home/vaultah/so/shadows/main.py 中)。

import sys, enum
sys.excepthook = spotlight('/home/vaultah/so/shadows')
enum.Enum('Faulty', 'a a', module=__name__)
Run Code Online (Sandbox Code Playgroud)

import sys, enum
sys.excepthook = shadow('/home/vaultah/cpython/Lib')
enum.Enum('Faulty', 'a a', module=__name__)
Run Code Online (Sandbox Code Playgroud)

给出相同的结果:

import enum
enum.Enum('Faulty', 'a a', module=__name__)
Run Code Online (Sandbox Code Playgroud)

有一种方法可以排除所有站点目录(安装了 3rd 方软件包的地方 - 请参阅site.getsitepackages

import sys, site, jinja2
sys.excepthook = shadow(*site.getsitepackages())
jinja2.Template('{%}')
# jinja2.exceptions.TemplateSyntaxError: unexpected '}'
#     Generates ~30 lines, but will only display 4
Run Code Online (Sandbox Code Playgroud)

注意:不要忘了恢复sys.excepthookSYS .__ excepthook__。不幸的是,您将无法使用上下文管理器“修补恢复”它。