当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《解析.py文件,读取AST,修改它,然后写回修改后的源代码》经验,为你挑选了6个好方法。

我想以编程方式编辑python源代码.基本上我想读取一个.py文件,生成AST,然后写回修改过的python源代码(即另一个.py文件).

有一些方法可以使用标准的python模块解析/编译python源代码,例如astcompiler.但是,我不认为它们中的任何一个都支持修改源代码的方法(例如删除此函数声明),然后回写修改python源代码.

更新:我想这样做的原因是我想为python 编写一个Mutation测试库,主要是通过删除语句/表达式,重新运行测试和查看什么中断.



1> Ryan..:

Pythoscope对它自动生成的测试用例执行此操作,就像python 2.6 的2to3工具一样(它将python 2.x源转换为python 3.x源).

这两个工具都使用lib2to3库,它是python解析器/编译器机器的一个实现,当它从源 - > AST - >源进行往返时可以保留源代码中的注释.

如果您想进行更多重构,那么此项目可能会满足您的需求.

该AST模块是你的其他选择,并有一个旧例子如何"unparse"语法树回代码(使用解析器模块).但是ast当对代码进行AST转换然后转换为代码对象时,该模块更有用.

该redbaron项目也可能是一个不错的选择(HT泽维尔Combelle)


仍然维护了unsarse示例,这里是更新的py3k版本:http://hg.python.org/cpython/log/tip/Tools/parser/unparse.py
我认为redbaron https://redbaron.readthedocs.org/en/latest/将是一个很好的补充

2> Brian..:

内置的ast模块似乎没有转换回源的方法.但是,这里的codegen模块为ast提供了一个漂亮的打印机,可以让你这样做.例如.

import ast
import codegen

expr="""
def foo():
   print("hello world")
"""
p=ast.parse(expr)

p.body[0].body = [ ast.parse("return 42").body[0] ] # Replace function body with "return 42"

print(codegen.to_source(p))

这将打印:

def foo():
    return 42

请注意,您可能会丢失确切的格式和注释,因为这些不会被保留.

但是,您可能不需要.如果您只需要执行替换的AST,则只需在ast上调用compile()并执行生成的代码对象即可.


对于将来使用它的任何人来说,codegen很大程度上已经过时并且有一些bug.我修了几个; 我将此作为github上的要点:https://gist.github.com/791312
[astor](https://pypi.org/project/astor/)似乎是codegen的维护继任者

3> Brandon Rhod..:

您可能不需要重新生成源代码.当然,这对我来说有点危险,因为你实际上没有解释为什么你认为你需要生成一个充满代码的.py文件; 但:

如果你想生成一个人们会实际使用的.py文件,也许这样他们可以填写一个表单并获得一个有用的.py文件插入到他们的项目中,那么你不想把它改成AST和因为你将失去所有的格式(想想通过将相关的行集合在一起使得Python具有可读性的空行)(ast节点有linenocol_offset属性)注释.相反,您可能希望使用模板引擎(例如,Django模板语言,旨在使模板甚至文本文件变得容易)来自定义.py文件,或者使用Rick Copeland的MetaPython扩展.

如果您在编译模块期间尝试进行更改,请注意您不必一直回到文本; 你可以直接编译AST而不是把它变回.py文件.

但几乎在任何情况下,你可能都试图做一些动态的东西,像Python这样的语言实际上很容易,而不用编写新的.py文件!如果你扩展你的问题让我们知道你真正想要完成什么,那么新的.py文件可能根本不会涉及答案; 我已经看到数百个Python项目正在处理数百个真实世界的东西,而且只需编写一个.py文件就不需要其中一个.所以,我必须承认,我有点怀疑你发现了第一个好的用例.:-)

更新:既然你已经解释了你想要做的事情,那么我很想在AST上进行操作.你会希望通过删除而不是文件的行来改变(这可能导致半语句只是死于SyntaxError),而是整个语句 - 还有什么比在AST中更好的地方呢?



4> argentpepper..:

在一个不同的答案我建议使用该astor包,但我已经发现了一个更新的AST解析包,称为astunparse:

>>> import ast
>>> import astunparse
>>> print(astunparse.unparse(ast.parse('def foo(x): return 2 * x')))


def foo(x):
    return (2 * x)

我在Python 3.5上测试了这个.



5> ViFI..:

ast模块的帮助下,解析和修改代码结构当然是可能的,我稍后会在一个例子中展示它.但是,ast单独使用模块无法写回修改后的源代码.此工作还有其他模块,例如此处的模块.

注意:下面的示例可以作为ast模块使用的入门教程处理,但有关使用ast模块的更全面的指南,请参阅Green Tree snakes教程和模块的官方文档ast.

简介ast:

>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> exec(compile(tree, filename="", mode="exec"))
Hello Python!!

您只需调用API即可解析python代码(以字符串表示)ast.parse().这将返回抽象语法树(AST)结构的句柄.有趣的是,您可以编译回这个结构并执行它,如上所示.

另一个非常有用的API是以ast.dump()字符串形式转储整个AST.它可用于检查树结构,在调试时非常有用.例如,

在Python 2.7上:

>>> import ast
>>> tree = ast.parse("print 'Hello Python!!'")
>>> ast.dump(tree)
"Module(body=[Print(dest=None, values=[Str(s='Hello Python!!')], nl=True)])"

在Python 3.5上:

>>> import ast
>>> tree = ast.parse("print ('Hello Python!!')")
>>> ast.dump(tree)
"Module(body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Str(s='Hello Python!!')], keywords=[]))])"

请注意Python 2.7与Python 3.5中print语句的语法差异以及各个树中AST节点类型的差异.


如何修改代码使用ast:

现在,让我们来看一个按ast模块修改python代码的例子.修改AST结构的主要工具是ast.NodeTransformerclass.每当需要修改AST时,他/她需要从中进行子类化并相应地编写节点转换.

对于我们的示例,让我们尝试编写一个简单的实用程序,它将Python 2,print语句转换为Python 3函数调用.

打印语句到Fun call converter实用程序:print2to3.py:

#!/usr/bin/env python
'''
This utility converts the python (2.7) statements to Python 3 alike function calls before running the code.

USAGE:
     python print2to3.py 
'''
import ast
import sys

class P2to3(ast.NodeTransformer):
    def visit_Print(self, node):
        new_node = ast.Expr(value=ast.Call(func=ast.Name(id='print', ctx=ast.Load()),
            args=node.values,
            keywords=[], starargs=None, kwargs=None))
        ast.copy_location(new_node, node)
        return new_node

def main(filename=None):
    if not filename:
        return

    with open(filename, 'r') as fp:
        data = fp.readlines()
    data = ''.join(data)
    tree = ast.parse(data)

    print "Converting python 2 print statements to Python 3 function calls"
    print "-" * 35
    P2to3().visit(tree)
    ast.fix_missing_locations(tree)
    # print ast.dump(tree)

    exec(compile(tree, filename="p23", mode="exec"))

if __name__ == '__main__':
    if len(sys.argv) <=1:
        print ("\nUSAGE:\n\t print2to3.py ")
        sys.exit(1)
    else:
        main(sys.argv[1])

可以在小示例文件上尝试此实用程序,例如下面的一个,它应该可以正常工作.

测试输入文件:py2.py

class A(object):
    def __init__(self):
        pass

def good():
    print "I am good"

main = good

if __name__ == '__main__':
    print "I am in main"
    main()

请注意,上述转换仅用于ast教程目的,在实际情况下,必须查看所有不同的场景,例如print " x is %s" % ("Hello Python").



6> paluh..:

我最近创建的非常稳定(核心经过了很好的测试)和可扩展的代码片段,它从ast树生成代码:https://github.com/paluh/code-formatter.

我正在使用我的项目作为小vim插件的基础(我每天都在使用它),因此我的目标是生成非常好的和可读的python代码.

PS我试图扩展,codegen但它的架构基于ast.NodeVisitor接口,因此格式化程序(visitor_方法)只是函数.我发现这种结构非常有限并且难以优化(在长和嵌套表达式的情况下,更容易保留对象树并缓存一些部分结果 - 换句话说,如果你想搜索最佳布局,你可以达到指数复杂性). codegen由于每一块光彦的工作(这是我读过)是写得很好,简洁.

推荐阅读
pan2502851807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有