【问题标题】:Python comment-preserving parsing using only builtin libraries?仅使用内置库的 Python 保留注释解析?
【发布时间】:2022-01-19 05:25:11
【问题描述】:

我编写了一个库,仅使用 astinspect 库来解析和发出 [在 Python astor] 内部 Python 构造。

我终于意识到我真的需要保留 cmets。最好不要诉诸RedBaronLibCST;因为我只需要发出未更改的评论;是否有一种简洁仅使用 stdlib 来保留注释解析/发出 Python 源代码的方法

【问题讨论】:

  • inspect.getsource() 返回包含 cmets 的对象的源代码。这是你需要的吗?
  • 否,因为我正在修改 AST 节点,更改:文档字符串; ast.Assign; ast.AnnAssign;和ast.FunctionDef/ast.AsyncFunctionDef。推断类型,将它们添加为类型注释 xor 注释;在文档字符串格式之间转换(包括添加/删除类型);并更新函数定义的return 属性。

标签: python parsing syntax abstract-syntax-tree concrete-syntax-tree


【解决方案1】:

可以通过使用标记器捕获注释将它们合并回生成的源代码来保留它们。

给定一个程序变量中的玩具程序,我们可以演示 cmets 如何在 AST 中丢失:

import ast

program = """
# This comment lost
p1v = 4 + 4
p1l = ['a', # Implicit line joining comment for a lost
       'b'] # Ending comment for b lost
def p1f(x):
    "p1f docstring"
    # Comment in function p1f lost
    return x
print(p1f(p1l), p1f(p1v))
"""
tree = ast.parse(program)
print('== Full program code:')
print(ast.unparse(tree))

输出显示所有 cmets 消失:

== Full program code:
p1v = 4 + 4
p1l = ['a', 'b']

def p1f(x):
    """p1f docstring"""
    return x
print(p1f(p1l), p1f(p1v))

但是,如果我们使用分词器扫描 cmets,我们可以 使用它来合并 cmets:

from io import StringIO
import tokenize

def scan_comments(source):
    """ Scan source code file for relevant comments
    """
    # Find token for comments
    for k,v in tokenize.tok_name.items():
        if v == 'COMMENT':
            comment = k
            break
    comtokens = []
    with StringIO(source) as f:
        tokens = tokenize.generate_tokens(f.readline)
        for token in tokens:
            if token.type != comment:
                continue
            comtokens += [token]
    return comtokens

comtokens = scan_comments(program)
print('== Comment after p1l[0]\n\t', comtokens[1])

输出(编辑为分割长线):

== Comment after p1l[0]
     TokenInfo(type=60 (COMMENT),
               string='# Implicit line joining comment for a lost',
               start=(4, 12), end=(4, 54),
               line="p1l = ['a', # Implicit line joining comment for a lost\n")

使用ast.unparse() 的略微修改版本,替换 方法 maybe_newline()traverse() 修改版本, 你应该能够在他们的所有 cmets 中合并回来 大致位置,使用评论中的位置信息 扫描仪(开始变量),结合来自 AST;大多数节点都有lineno 属性。

不完全是。参见例如列表变量赋值。这 源代码分为两行,但ast.unparse() 只生成一行(参见第二个代码段中的输出)。

另外,您需要确保更新 AST 中的位置信息 添加代码后使用ast.increment_lineno()

似乎有更多的电话 maybe_newline() 可能需要在库代码(或其 替换)。

【讨论】:

    猜你喜欢
    • 2011-05-27
    • 2016-02-08
    • 1970-01-01
    • 1970-01-01
    • 2016-02-20
    • 1970-01-01
    • 2018-05-10
    • 2012-10-20
    • 2021-01-24
    相关资源
    最近更新 更多