解析一个.py文件,读取AST,修改AST,然后写回已修改的源代码。

215
我想要以编程的方式编辑 Python 源代码。基本上,我想要读取一个 .py 文件,生成 AST,然后将修改后的 Python 源代码写回(即另一个.py文件)。
有一些使用标准 Python 模块解析/编译 Python 源代码的方法,例如astcompiler。但是,我认为它们都不支持修改源代码的方式(例如删除此函数声明),然后将修改后的 Python 源代码写回。
更新:我想这样做的原因是我想为 Python 编写一个变异测试库,主要是通过删除语句 / 表达式、重新运行测试并查看哪些部分出错来实现的。

5
自版本2.6起被弃用:编译器包已在Python 3.0中移除。 - dfa
3
哇塞!我想用相同的技术(具体来说是创建一个 nose 插件)为 Python 制作一个变异测试器,你打算开源吗? - Ryan
2
@Ryan 是的,我会开源我创建的任何东西。我们应该在这方面保持联系。 - Amandasaurus
1
你在突变中运行任何遗传算法吗? :P - chiffa
1
Macropy提供了在导入时操作抽象语法树的语法糖。 - jfs
显示剩余2条评论
14个回答

2
我们有一个类似的需求,其他答案没有解决。因此,我们创建了一个库ASTTokens,它使用astastroid模块生成的AST树,并将其标记为原始源代码中的文本范围。
它不直接修改代码,但是很容易在其上添加,因为它会告诉您需要修改的文本范围。
例如,这个示例将一个函数调用包装在WRAP(...)中,保留注释和其他所有内容:
example = """
def foo(): # Test
  '''My func'''
  log("hello world")  # Print
"""

import ast, asttokens
atok = asttokens.ASTTokens(example, parse=True)

call = next(n for n in ast.walk(atok.tree) if isinstance(n, ast.Call))
start, end = atok.get_text_range(call)
print(atok.text[:start] + ('WRAP(%s)' % atok.text[start:end])  + atok.text[end:])

产生:
def foo(): # Test
  '''My func'''
  WRAP(log("hello world"))  # Print

希望这有所帮助!

1

一个程序转换系统是一种工具,可以解析源代码文本,构建ASTs,并允许您使用源到源转换来修改它们(“如果您看到这个模式,请将其替换为该模式”)。这样的工具非常适合对现有源代码进行突变,这些代码仅是“如果您看到此模式,请替换为模式变体”。

当然,您需要一个能够解析您感兴趣的语言并仍然进行模式导向转换的程序转换引擎。我们的DMS软件重构工具包可以做到这一点,并处理Python和其他各种语言。

查看此SO答案,其中有一个Python的DMS解析AST捕获注释的示例。DMS可以更改AST并重新生成有效文本,包括注释。您可以要求它使用自己的格式约定(您可以更改这些)对AST进行漂亮打印,或者进行“忠实打印”,该打印使用原始行和列信息以最大程度地保留原始布局(在插入新代码的位置上不可避免地会有一些布局变化)。

要使用DMS为Python实现“突变”规则,可以编写以下内容:

rule mutate_addition(s:sum, p:product):sum->sum =
  " \s + \p " -> " \s - \p"
 if mutate_this_place(s);

这个规则以语法正确的方式将“+”替换为“-”; 它操作于AST,因此不会触及看起来正确的字符串或注释。 "mutate_this_place"上的额外条件是让您控制此操作发生的频率; 您不希望突变程序中的每个位置。

显然,您需要更多类似于此的规则,以检测各种代码结构,并用突变版本替换它们。 DMS很高兴应用一组规则。 突变后的AST然后被漂亮地打印出来。


4
因为它推广了一个非常昂贵的闭源工具。 - Zoran Pavlovic
检查请求者的编辑/评论。我怀疑一个开源库能否与您提出的使用闭源工具的解决方案兼容。 - Zoran Pavlovic
2
@Zoran:他没有说他有一个开源库。他说他想修改Python源代码(使用ASTs),而他找到的解决方案并没有做到这一点。这就是一个解决方案。你难道不认为人们在像Python或Java这样的语言编写的程序中使用商业工具吗? - Ira Baxter
@Zoran:有些人会认为DMS很贵;它肯定不属于100美元的范畴。另一方面,它可以在开箱即用时完成许多其他“免费”或廉价工具无法完成的任务,包括可靠地解析和转换Python。如果你认为你的时间是免费的,并且你有很多时间,那么DMS就显得很贵了。如果你需要解决一个问题,找不到其他答案,也不想从头开始构建所有东西,那么你对其成本的看法可能会大不相同。这是付费软件存在的经典原因,与一切都是免费软件不同。 - Ira Baxter
1
我不是一个踩贴者,但这篇帖子读起来有点像广告。为了改善回答,你可以披露你与该产品有关联。 - wim
显示剩余5条评论

1

我曾经使用baron来完成这个任务,但现在已经转换到parso,因为它与现代python兼容。它非常好用。

我也需要这个来进行变异测试。使用parso制作一个变异测试器非常简单,请查看我的代码https://github.com/boxed/mutmut


0

我已经编写了几个实用程序来完成这种操作,而在每种情况下,我选择的工具都是libcst。Instagram创建了这个库来操作他们的Python代码库,例如插入类型注释。尽管它没有使用AST,而是CST,但结构非常相似,而且易于使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接