我想以编程方式编辑python源代码。基本上我想读取一个.py文件,生成AST,然后写回修改后的python源代码(即另一个.py文件)。
有一些方法可以使用标准的python模块(如ast或compiler)来解析/编译python源代码。但是,我认为它们都不支持修改源代码的方法(例如删除这个函数声明),然后再写回修改的python源代码。
更新:我想这样做的原因是我想为python写一个突变测试库,主要是通过删除语句/表达式,重新运行测试,看看有什么破坏。
我想以编程方式编辑python源代码。基本上我想读取一个.py文件,生成AST,然后写回修改后的python源代码(即另一个.py文件)。
有一些方法可以使用标准的python模块(如ast或compiler)来解析/编译python源代码。但是,我认为它们都不支持修改源代码的方法(例如删除这个函数声明),然后再写回修改的python源代码。
更新:我想这样做的原因是我想为python写一个突变测试库,主要是通过删除语句/表达式,重新运行测试,看看有什么破坏。
当前回答
不幸的是,上面的答案实际上没有一个同时满足这两个条件
保持周围源代码的语法完整性(例如保留注释,其他类型的代码格式) 实际上使用AST(而不是CST)。
我最近写了一个小工具包来进行纯基于AST的重构,称为重构。例如,如果你想用42替换所有占位符,你可以简单地像这样写一个规则;
class Replace(Rule):
def match(self, node):
assert isinstance(node, ast.Name)
assert node.id == 'placeholder'
replacement = ast.Constant(42)
return ReplacementAction(node, replacement)
它会找到所有可接受的节点,用新节点替换它们并生成最终的表单;
--- test_file.py
+++ test_file.py
@@ -1,11 +1,11 @@
def main():
- print(placeholder * 3 + 2)
- print(2 + placeholder + 3)
+ print(42 * 3 + 2)
+ print(2 + 42 + 3)
# some commments
- placeholder # maybe other comments
+ 42 # maybe other comments
if something:
other_thing
- print(placeholder)
+ print(42)
if __name__ == "__main__":
main()
其他回答
我们也有类似的需求,这里的其他答案并没有解决这个问题。因此,我们为此创建了一个库ASTTokens,它采用AST或astroid模块生成的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
希望这能有所帮助!
Pythoscope对它自动生成的测试用例执行此操作,就像python 2.6的2to3工具一样(它转换python 2。X源代码转换为python 3。x源)。
这两个工具都使用lib2to3库,它是python解析器/编译器机制的实现,可以在从source -> AST -> source循环绊倒源代码时保留源代码中的注释。
如果您想进行更多的重构(如转换),rope项目可以满足您的需求。
ast模块是另一个选择,还有一个关于如何将语法树“解解析”回代码的旧示例(使用解析器模块)。但是ast模块在对随后转换为代码对象的代码进行ast转换时更有用。
红男爵计划也可能是个不错的选择(泽维尔·康贝尔)
如果你在2019年看到这个,那么你可以使用这个libcst 包中。它的语法类似ast。这就像一个魅力,并保留了代码结构。这对于你必须保留注释、空格、换行符等的项目是很有帮助的。
如果您不需要关心保留注释、空格和其他内容,那么ast和astor的组合可以很好地工作。
程序转换系统是一种工具,它可以解析源文本,构建ast,允许您使用源到源转换(“如果您看到这个模式,请将其替换为那个模式”)来修改它们。这样的工具非常适合对现有源代码进行突变,即“如果看到此模式,请用模式变体替换”。
当然,您需要一个程序转换引擎,它可以解析您感兴趣的语言,并仍然执行面向模式的转换。我们的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随后被漂亮地打印出来。
另一种回答建议使用密码原,它似乎已被阿斯特取代。PyPI上的astor版本(撰写本文时的版本为0.5)似乎也有点过时,因此您可以按如下方式安装astor的开发版本。
pip install git+https://github.com/berkerpeksag/astor.git#egg=astor
然后你可以使用阿斯特。to_source将Python AST转换为人类可读的Python源代码:
>>> import ast
>>> import astor
>>> print(astor.to_source(ast.parse('def foo(x): return 2 * x')))
def foo(x):
return 2 * x
我已经在Python 3.5上进行了测试。