[英]Get traceback information including SyntaxError from compile()
似乎SyntaxError
S(和TypeError
由凸起或多个) compile()
函数中不包括由返回的堆栈跟踪sys.exc_info()
但被打印为使用格式化输出的一部分traceback.print_exc
。
例如,给定以下代码(其中filename
是包含带有$ flagrant syntax error
行的Python代码的文件的名称):
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()
我得到以下输出(其中<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
我有三个问题:
sys.exc_info()
的回溯不包括生成SyntaxError
的帧? traceback.print_exc
如何获取缺少的帧信息? SyntaxError
的最佳方法是什么? 是否需要使用filename
和SyntaxError
异常对象本身手动构造最后一个list元素(即表示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()
请注意,“相当丑陋”,我的意思是这个解决方法将应该是单参数的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
这里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')
另请参阅traceback.print_exception()
的文档:
(3)如果type是
SyntaxError
并且value具有适当的格式,它将打印出现语法错误的行,并使用插入符指示错误的大致位置。
此类的实例具有
filename
,lineno
,offset
和text
属性,lineno
于访问详细信息。 异常实例的str()
仅返回消息。
具有语法错误的行不包含在回溯中仅是逻辑的; 无法执行具有语法错误的代码,因此不会为其创建任何执行帧。 而最常见的执行框架是compile()
函数抛出异常。
因此,你坚持你的“丑陋”方法; 它是处理SyntaxError
异常的正确方法。 但是,属性已记录在案。
注意, exception.message
通常设置为exception.args[0]
,而str(exception)
通常会给你相同的消息(如果args
更长,你得到str(exception.args)
,尽管有些异常类型提供了自定义__str__
通常只给你exception.args[0]
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.