繁体   English   中英

不同的语法错误隐藏 output 中的行

[英]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异常:

  1. 解析时,由 Python 语法驱动
  2. 从解析结果创建抽象语法树 (AST) 时; AST 驱动编译器。

这是因为 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属性filenamelineno (行号)、 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM