如何限制 python 回溯到特定文件
How to limit python traceback to specific files
我编写了很多使用外部库的 Python 代码。我经常会写一个错误,当我 运行 代码时,我会在 Python 控制台中得到一个很长的回溯。 99.999999% 的时间是由于我的代码中的编码错误,而不是因为包中的错误。但是回溯一直到包代码中的错误行,要么需要大量滚动回溯才能找到我写的代码,要么回溯深入到包中以至于我自己的代码没有' t甚至出现在回溯中。
有没有办法 "black-box" 包代码,或者以某种方式只显示我的代码中的回溯行?我希望能够向系统指定我希望从中查看回溯的目录或文件。
traceback.extract_tb(tb) 会 return 错误帧的元组格式 (file, line_no, type, error_statement) ,你可以玩格式化追溯。另请参阅 https://pymotw.com/2/sys/exceptions.html
import sys
import traceback
def handle_exception(ex_type, ex_info, tb):
print ex_type, ex_info, traceback.extract_tb(tb)
sys.excepthook = handle_exception
为了打印您自己的堆栈跟踪,您需要自己处理所有未处理的异常;这就是 sys.excepthook
变得得心应手的方式。
这个函数的签名是sys.excepthook(type, value, traceback)
,它的工作是:
This function prints out a given traceback and exception to sys.stderr
.
因此,只要您可以使用回溯并仅提取您关心的部分就可以了。测试框架经常这样做;他们有自定义 assert
函数,通常不会出现在回溯中,换句话说,他们会跳过属于测试框架的帧。此外,在这些情况下,测试通常也由测试框架启动。
您最终得到如下所示的回溯:
[ custom assert code ]
+ ... [ code under test ] ...
+ [ test runner code ]
如何识别您的代码。
您可以在您的代码中添加一个全局变量:
__mycode = True
然后识别框架:
def is_mycode(tb):
globals = tb.tb_frame.f_globals
return globals.has_key('__mycode')
如何提取帧。
- 跳过与您无关的帧(例如自定义断言代码)
- 确定代码中有多少帧 ->
length
提取 length
帧
def mycode_traceback_levels(tb):
length = 0
while tb and is_mycode(tb):
tb = tb.tb_next
length += 1
return length
示例处理程序。
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))
安装处理程序:
sys.excepthook = handle_exception
下一步是什么?
如果您仍想了解有关您自己代码之外的故障位置的信息,您可以调整 length
以添加一个或多个级别。
正如其他人所建议的,您可以使用 sys.excepthook
:
This function prints out a given traceback and exception to sys.stderr.
When an exception is raised and uncaught, the interpreter calls sys.excepthook with three arguments, the exception class, exception instance, and a traceback object. In an interactive session this happens just before control is returned to the prompt; in a Python program this happens just before the program exits. The handling of such top-level exceptions can be customized by assigning another three-argument function to 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
他们都使用 traceback.extract_tb
. It returns "a list of “pre-processed” stack trace entries extracted from the traceback object"; all of them are instances of traceback.FrameSummary
(命名元组)。每个 traceback.FrameSummary
对象都有一个 filename
字段,用于存储相应文件的绝对路径。我们检查它是否以作为单独函数参数提供的任何目录路径开头,以确定我们是否需要排除条目(或保留它)。
这是一个例子:
标准库中的 enum
模块不允许重复使用密钥,
import enum
enum.Enum('Faulty', 'a a', module=__name__)
产量
Traceback (most recent call last):
File "/home/vaultah/so/shadows/main.py", line 23, in <module>
enum.Enum('Faulty', 'a a', module=__name__)
File "/home/vaultah/cpython/Lib/enum.py", line 243, in __call__
return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
File "/home/vaultah/cpython/Lib/enum.py", line 342, in _create_
classdict[member_name] = member_value
File "/home/vaultah/cpython/Lib/enum.py", line 72, in __setitem__
raise TypeError('Attempted to reuse key: %r' % key)
TypeError: Attempted to reuse key: 'a'
我们可以将堆栈跟踪条目限制在我们的代码中(在 /home/vaultah/so/shadows/main.py 中)。
import sys, enum
sys.excepthook = spotlight('/home/vaultah/so/shadows')
enum.Enum('Faulty', 'a a', module=__name__)
和
import sys, enum
sys.excepthook = shadow('/home/vaultah/cpython/Lib')
enum.Enum('Faulty', 'a a', module=__name__)
给出相同的结果:
File "/home/vaultah/so/shadows/main.py", line 22, in <module>
enum.Enum('Faulty', 'a a', module=__name__)
TypeError: Attempted to reuse key: 'a'
有一种方法可以排除所有站点目录(安装第 3 方包的位置 - 请参阅 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
Note: Don't forget to restore sys.excepthook from sys.__excepthook__. Unfortunately, you won't be able to "patch-restore" it using a context manager.
我编写了很多使用外部库的 Python 代码。我经常会写一个错误,当我 运行 代码时,我会在 Python 控制台中得到一个很长的回溯。 99.999999% 的时间是由于我的代码中的编码错误,而不是因为包中的错误。但是回溯一直到包代码中的错误行,要么需要大量滚动回溯才能找到我写的代码,要么回溯深入到包中以至于我自己的代码没有' t甚至出现在回溯中。
有没有办法 "black-box" 包代码,或者以某种方式只显示我的代码中的回溯行?我希望能够向系统指定我希望从中查看回溯的目录或文件。
traceback.extract_tb(tb) 会 return 错误帧的元组格式 (file, line_no, type, error_statement) ,你可以玩格式化追溯。另请参阅 https://pymotw.com/2/sys/exceptions.html
import sys
import traceback
def handle_exception(ex_type, ex_info, tb):
print ex_type, ex_info, traceback.extract_tb(tb)
sys.excepthook = handle_exception
为了打印您自己的堆栈跟踪,您需要自己处理所有未处理的异常;这就是 sys.excepthook
变得得心应手的方式。
这个函数的签名是sys.excepthook(type, value, traceback)
,它的工作是:
This function prints out a given traceback and exception to
sys.stderr
.
因此,只要您可以使用回溯并仅提取您关心的部分就可以了。测试框架经常这样做;他们有自定义 assert
函数,通常不会出现在回溯中,换句话说,他们会跳过属于测试框架的帧。此外,在这些情况下,测试通常也由测试框架启动。
您最终得到如下所示的回溯:
[ custom assert code ]
+ ... [ code under test ] ...
+ [ test runner code ]
如何识别您的代码。
您可以在您的代码中添加一个全局变量:
__mycode = True
然后识别框架:
def is_mycode(tb):
globals = tb.tb_frame.f_globals
return globals.has_key('__mycode')
如何提取帧。
- 跳过与您无关的帧(例如自定义断言代码)
- 确定代码中有多少帧 ->
length
提取
length
帧def mycode_traceback_levels(tb): length = 0 while tb and is_mycode(tb): tb = tb.tb_next length += 1 return length
示例处理程序。
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))
安装处理程序:
sys.excepthook = handle_exception
下一步是什么?
如果您仍想了解有关您自己代码之外的故障位置的信息,您可以调整 length
以添加一个或多个级别。
正如其他人所建议的,您可以使用 sys.excepthook
:
This function prints out a given traceback and exception to sys.stderr.
When an exception is raised and uncaught, the interpreter calls sys.excepthook with three arguments, the exception class, exception instance, and a traceback object. In an interactive session this happens just before control is returned to the prompt; in a Python program this happens just before the program exits. The handling of such top-level exceptions can be customized by assigning another three-argument function to 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
他们都使用 traceback.extract_tb
. It returns "a list of “pre-processed” stack trace entries extracted from the traceback object"; all of them are instances of traceback.FrameSummary
(命名元组)。每个 traceback.FrameSummary
对象都有一个 filename
字段,用于存储相应文件的绝对路径。我们检查它是否以作为单独函数参数提供的任何目录路径开头,以确定我们是否需要排除条目(或保留它)。
这是一个例子:
标准库中的 enum
模块不允许重复使用密钥,
import enum
enum.Enum('Faulty', 'a a', module=__name__)
产量
Traceback (most recent call last):
File "/home/vaultah/so/shadows/main.py", line 23, in <module>
enum.Enum('Faulty', 'a a', module=__name__)
File "/home/vaultah/cpython/Lib/enum.py", line 243, in __call__
return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
File "/home/vaultah/cpython/Lib/enum.py", line 342, in _create_
classdict[member_name] = member_value
File "/home/vaultah/cpython/Lib/enum.py", line 72, in __setitem__
raise TypeError('Attempted to reuse key: %r' % key)
TypeError: Attempted to reuse key: 'a'
我们可以将堆栈跟踪条目限制在我们的代码中(在 /home/vaultah/so/shadows/main.py 中)。
import sys, enum
sys.excepthook = spotlight('/home/vaultah/so/shadows')
enum.Enum('Faulty', 'a a', module=__name__)
和
import sys, enum
sys.excepthook = shadow('/home/vaultah/cpython/Lib')
enum.Enum('Faulty', 'a a', module=__name__)
给出相同的结果:
File "/home/vaultah/so/shadows/main.py", line 22, in <module>
enum.Enum('Faulty', 'a a', module=__name__)
TypeError: Attempted to reuse key: 'a'
有一种方法可以排除所有站点目录(安装第 3 方包的位置 - 请参阅 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
Note: Don't forget to restore sys.excepthook from sys.__excepthook__. Unfortunately, you won't be able to "patch-restore" it using a context manager.