[英]Different syntax errors hide line in output
我有一个调用compile
的脚本。
try:
code = compile('3 = 3', 'test', 'exec')
except Exception as e:
sys.stderr.write(''.join(traceback.format_exception_only(type(e), e)))
3 = 3
结果:
File "test", line 1
SyntaxError: can't assign to literal
而3 = 3a
实际上打印该行
File "test", line 1
3 = 3a
^
SyntaxError: invalid syntax
知道为什么吗?
Python 在两个地方产生SyntaxError
异常:
这是因为 Python 语法有一些特殊情况,在这些情况下更容易保持语法简单,但在解析完成后进行额外检查,并构建完成额外语法检查的 AST。
赋值就是其中之一,因为=
符号左侧允许的规则与右侧允许的规则不同,但仍然密切相关。 左侧是目标端,目标可以像列表或元组一样结构化(解包分配),您可以分配给属性或索引操作( listobj[1] =...
等)。 但是要让解析器检测到目标实际上是文字而不是变量名或属性等,需要一个非常不同的解析器结构,所以这留给了 AST。
因此,您的3 = 3
错误通过了解析阶段,但随后在后面的 AST“分配目标”检查阶段失败,而3 = 3a
落在解析器阶段(其中3a
很容易被视为错误)。
为了给你一个好的语法错误,解析器引发的异常包含异常中的源代码行:
>>> try:
... code = compile('3 = 3a', 'test', 'exec')
... except Exception as e:
... print(repr(e))
...
SyntaxError('invalid syntax', ('test', 1, 6, '3 = 3a\n'))
注意异常中的('test', 1, 6, '3 = 3a\n')
元组; 这些可以通过SyntaxError
属性filename
、 lineno
(行号)、 offset
(列偏移量)和源代码行本身的text
获得。 对于解析器,这很容易提供,因为它可以访问源代码。
但是AST没有源代码。 它只有文件名、行号、列和解析树对象。 它没有原始源文本。 它通常会尝试从文件名中读取它,但test
实际上并不是一个文件。 所以该行是空的:
>>> try:
... code = compile('3 = 3', 'test', 'exec')
... except Exception as e:
... print(repr(e))
...
SyntaxError('cannot assign to literal', ('test', 1, 1, ''))
您可以对此进行测试并通过将SyntaxError
异常替换为新异常并将空字符串替换为源文本来修复它:
>>> source = '3 = 3'
>>> try:
... code = compile(source, 'test', 'exec')
... except Exception as e:
... if isinstance(e, SyntaxError) and not e.text:
... sline = source.splitlines(True)[e.lineno - 1]
... e = SyntaxError(e.msg, (e.filename, e.lineno, e.offset, sline))
... sys.stderr.write(''.join(traceback.format_exception_only(type(e), e)))
...
File "test", line 1
3 = 3
^
SyntaxError: cannot assign to literal
请注意,对于多行源字符串,您希望将该源拆分为行并使用.lineno
属性到 select 指定的源行。
另一种方法是将源代码写入一个临时文件名,并将该文件名传递给compile()
以便在构建 AST 时发现SyntaxError
异常时,Python 可以打开该临时文件并找到相应的文本行。
请注意,当您使用特殊文件名'<string>'
时,不会尝试查找行的源代码,并且e.text
设置为None
:
>>> try:
... code = compile('3 = 3', '<string>', 'exec')
... except Exception as e:
... print(repr(e))
...
SyntaxError('cannot assign to literal', ('<string>', 1, 1, None))
当.text
属性设置为None
时, traceback
模块放弃打印 line-and-marker 部分。
如果您对 Python 语法解析器无法检测赋值目标中的文字的确切原因感兴趣,您可能会对 Guido van Rossum为 Python 编写不同解析器所做的工作感兴趣,其中包括对当前解析器工作原理的说明它的方式以及替代解析器 model 如何避免这些问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.