Kyl*_*and 8 python exception-handling traceback
看来,SyntaxErrorS(和TypeErrorS)由凸起compile()功能被不包括在堆栈跟踪返回的sys.exc_info(),但被打印为使用格式化输出的一部分traceback.print_exc.
例如,给定以下代码(其中filename包含带有该行的Python代码的文件的名称$ flagrant syntax error):
import sys
from traceback import extract_tb
try:
with open(filename) as f:
code = compile(f.read(), filename, "exec")
except:
print "using sys.exc_info:"
tb_list = extract_tb(sys.exc_info()[2])
for f in tb_list:
print f
print "using traceback.print_exc:"
from traceback import print_exc
print_exc()
Run Code Online (Sandbox Code Playgroud)
我得到以下输出(其中<scriptname>是包含上述代码的脚本的名称):
using sys.exc_info:
('<scriptname>', 6, 'exec_file', 'code = compile(f.read(), filename, "exec")')
using traceback.print_exc:
Traceback (most recent call last):
File "<scriptname>", line 6, in exec_file
code = compile(f.read(), filename, "exec")
File "<filename>", line 3
$ flagrant syntax error
^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)
我有三个问题:
sys.exc_info()包括SyntaxError生成的框架?traceback.print_exc获取丢失的帧信息?SyntaxError列表中的?是否SyntaxError需要使用filename和SyntaxError异常对象本身手动构造最后一个列表元素(即来自堆栈帧的信息表示发生的位置)?对于上下文,这是我试图获得完整的堆栈跟踪提取的用例.
我有一个程序,它基本上exec通过一些包含用户编写的Python代码的文件来实现DSL .(无论这是否是一个良好的DSL实现策略,我或多或少地坚持它.)当遇到用户代码中的错误时,我会(在某些情况下)像解释器一样保存错误以后,而不是呕吐堆栈痕迹和死亡.所以我有一个ScriptExcInfo专门用来存储这些信息的课程.这是该类的__init__方法(稍微编辑过的版本),完成了上述问题的相当丑陋的解决方法:
def __init__(self, scriptpath, add_frame):
self.exc, tb = sys.exc_info()[1:]
self.tb_list = traceback.extract_tb(tb)
if add_frame:
# Note: I'm pretty sure that the names of the linenumber and
# message attributes are undocumented, and I don't think there's
# actually a good way to access them.
if isinstance(exc, TypeError):
lineno = -1
text = exc.message
else:
lineno = exc.lineno
text = exc.text
# '?' is used as the function name since there's no function context
# in which the SyntaxError or TypeError can occur.
self.tb_list.append((scriptpath, lineno, '?', text))
else:
# Pop off the frames related to this `exec` infrastructure.
# Note that there's no straightforward way to remove the unwanted
# frames for the above case where the desired frame was explicitly
# constructed and appended to tb_list, and in fact the resulting
# list is out of order (!!!!).
while scriptpath != self.tb_list[-1][0]:
self.tb_list.pop()
Run Code Online (Sandbox Code Playgroud)
请注意,"相当丑陋",我的意思是这个解决方法将应该是单参数的4行 __init__函数转换为需要两个参数并占用13行.
两种方法之间的唯一区别是print_exc()打印格式化的异常.对于SyntaxError包括格式化该异常中的信息的内容,其中包括导致问题的实际行.
对于回溯本身,print_exc()使用sys.exc_info()[2]与您已经使用的相同信息来生成回溯.换句话说,它不会获得比您已经做的更多的信息,但是您忽略了异常信息本身:
>>> import traceback
>>> try:
... compile('Hmmm, nope!', '<stdin>', 'exec')
... except SyntaxError as e:
... print ''.join(traceback.format_exception_only(type(e), e))
...
File "<stdin>", line 1
Hmmm, nope!
^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)
这traceback.format_exception_only()是一个用于traceback.print_exc()格式化异常值的未记录函数.所有信息都可供您在异常中提取:
>>> dir(e)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__getslice__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__unicode__', 'args', 'filename', 'lineno', 'message', 'msg', 'offset', 'print_file_and_line', 'text']
>>> e.args
('invalid syntax', ('<stdin>', 1, 11, 'Hmmm, nope!\n'))
>>> e.filename, e.lineno, e.offset, e.text
('<stdin>', 1, 11, 'Hmmm, nope!\n')
Run Code Online (Sandbox Code Playgroud)
另请参阅以下文档traceback.print_exception():
(3)如果type为
SyntaxError和value具有适当的格式,则打印出发生语法错误的行,并使用插入符号指示错误的大致位置.
这个类的实例有属性
filename,lineno,offset并且text为的细节更容易获得.str()异常实例仅返回消息.
具有语法错误的行不包含在回溯中仅是逻辑的; 无法执行具有语法错误的代码,因此不会为其创建任何执行帧.并且compile()函数抛出了异常,这是最底层的执行框架.
因此,你坚持你的"丑陋"方法; 这是处理SyntaxError异常的正确方法.但是,属性已记录在案.
请注意,exception.message通常设置为exception.args[0],str(exception) 通常会为您提供相同的消息(如果args您获得更长的时间str(exception.args),尽管某些异常类型提供了__str__通常只提供给您的自定义exception.args[0]).