从compile()获取包括SyntaxError在内的回溯信息

时间:2014-12-08 18:52:46

标签: python exception-handling traceback

基本问题

SyntaxError函数引发的TypeError s(和compile() s)似乎 包含在{{1}返回的堆栈跟踪中},但 使用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() 是包含上述代码的脚本的名称):

<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如何获取缺少的相框信息?
  • 保存&#34;提取&#34;的最佳方法是什么?列表中的追溯信息,包括traceback.print_exc?是否需要使用SyntaxErrorSyntaxError异常对象本身手动构建最后一个列表元素(即表示filename发生位置的堆栈帧中的信息)?

示例用例

对于上下文,这是我试图获得完整的堆栈跟踪提取的用例。

我有一个基本上通过SyntaxError包含用户编写的Python代码的文件来实现DSL的程序。 (无论这是否是一个很好的DSL实现策略,我或多或少地坚持使用它。)当遇到用户代码中的错误时,我会(在某些情况下)像解释器一样保存以后的错误,而不是呕吐堆栈跟踪和死亡。所以我有一个专门用来存储这些信息的exec课程。这是该类的ScriptExcInfo方法的一个(稍微编辑过的版本),上面提到了一个相当丑陋的解决方法:

__init__

请注意,&#34;相当难看,&#34;我的意思是这个解决方法将应该是单参数的4行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() 函数转换为需要两个参数并占用13行。

1 个答案:

答案 0 :(得分:8)

两种方法之间的唯一差异是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)如果类型SyntaxError并且值具有适当的格式,则会打印出现语法错误的行,并使用插入符号指示错误的大致位置。

SyntaxError documentation

  

此类的实例具有属性filenamelinenooffsettext,以便于访问详细信息。异常实例的str()仅返回消息。

回溯中不包含语法错误的行只是逻辑;无法执行具有语法错误的代码,因此不会为其创建任何执行帧。而compile()函数抛出异常,这是最底层的执行框架。

因此,你被困在丑陋的&#39;办法;这是处理SyntaxError例外的正确方法。但是,属性已记录。

请注意,exception.message通常设置为exception.args[0],而str(exception) 通常会为您提供相同的消息(如果args更长str(exception.args)相反,虽然某些例外类型提供的自定义__str__通常只会为您提供exception.args[0])。