在 Python 中向 AST 添加节点时,函数未添加到新行
Function isn't added to new line, when adding node to AST in Python
in Python 我尝试使用 AST 在源代码中的每个 for 循环之后添加打印语句。然而,问题是 print 语句没有添加到新行,而是添加到与 for 循环相同的行。添加 fix_missing_locations()
和 increment_lineno()
的各种组合没有帮助。我做错了什么?
import astor
import ast
class CodeInstrumentator(ast.NodeTransformer):
def get_print_stmt(self, lineno):
return ast.Call(
func=ast.Name(id='print', ctx=ast.Load()),
args=[ast.Num(n=lineno)],
keywords=[]
)
def insert_print(self, node):
node.body.insert(0, self.get_print_stmt(node.lineno))
def visit_For(self, node):
self.insert_print(node)
self.generic_visit(node)
return node
def main():
input_file = 'source.py'
try:
myAST = astor.parsefile(input_file)
except Exception as e:
raise e
CodeInstrumentator().visit(myAST)
instru_source = astor.to_source(myAST)
source_file = open('test.py', 'w')
source_file.write(instru_source)
if __name__ == "__main__":
main()
这个问题似乎被我遇到了类似的问题而放弃了,我终于找到了一个解决方案,所以我把它写下来以防它对某人有用。
首先,请注意 ASTOR 既不依赖也不依赖 lineno
或 col_offset
,因此使用 ast.fix_missing_locations(node)
、increment_lineno(node, n=1)
或 new_node = ast.copy_location(new_node, node)
不会有对输出代码有任何影响。
这就是说,问题是 Call
语句不是一个独立的操作,因此,ASTOR 将它应用于前一个节点(因为它是同一操作的一部分,但你写错了节点的 lineno
)。
然后,解决方案是使用 Expr
语句将 Call
语句用 void 调用包装起来:
def get_print_stmt(self, lineno):
return ast.Expr(value=ast.Call(
func=ast.Name(id='print', ctx=ast.Load()),
args=[ast.Num(n=lineno)],
keywords=[]
))
如果您编写的代码包含对函数的 void 调用,您会注意到它的 AST 表示已经包含 Expr
节点:
test_file.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# MAIN
#
my_func()
process_file.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function
def main():
tree = astor.code_to_ast.parse_file("test_file.py")
print("DUMP TREE")
print(astor.dump_tree(tree))
print("SOURCE")
print(astor.to_source(tree))
#
# MAIN
#
if __name__ == '__main__':
main()
输出
$ python process_file.py
DUMP TREE
Module(
body=[
Expr(value=Call(func=Name(id='my_func'), args=[], keywords=[], starargs=None, kwargs=None))])
SOURCE
my_func()
in Python 我尝试使用 AST 在源代码中的每个 for 循环之后添加打印语句。然而,问题是 print 语句没有添加到新行,而是添加到与 for 循环相同的行。添加 fix_missing_locations()
和 increment_lineno()
的各种组合没有帮助。我做错了什么?
import astor
import ast
class CodeInstrumentator(ast.NodeTransformer):
def get_print_stmt(self, lineno):
return ast.Call(
func=ast.Name(id='print', ctx=ast.Load()),
args=[ast.Num(n=lineno)],
keywords=[]
)
def insert_print(self, node):
node.body.insert(0, self.get_print_stmt(node.lineno))
def visit_For(self, node):
self.insert_print(node)
self.generic_visit(node)
return node
def main():
input_file = 'source.py'
try:
myAST = astor.parsefile(input_file)
except Exception as e:
raise e
CodeInstrumentator().visit(myAST)
instru_source = astor.to_source(myAST)
source_file = open('test.py', 'w')
source_file.write(instru_source)
if __name__ == "__main__":
main()
这个问题似乎被我遇到了类似的问题而放弃了,我终于找到了一个解决方案,所以我把它写下来以防它对某人有用。
首先,请注意 ASTOR 既不依赖也不依赖 lineno
或 col_offset
,因此使用 ast.fix_missing_locations(node)
、increment_lineno(node, n=1)
或 new_node = ast.copy_location(new_node, node)
不会有对输出代码有任何影响。
这就是说,问题是 Call
语句不是一个独立的操作,因此,ASTOR 将它应用于前一个节点(因为它是同一操作的一部分,但你写错了节点的 lineno
)。
然后,解决方案是使用 Expr
语句将 Call
语句用 void 调用包装起来:
def get_print_stmt(self, lineno):
return ast.Expr(value=ast.Call(
func=ast.Name(id='print', ctx=ast.Load()),
args=[ast.Num(n=lineno)],
keywords=[]
))
如果您编写的代码包含对函数的 void 调用,您会注意到它的 AST 表示已经包含 Expr
节点:
test_file.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# MAIN
#
my_func()
process_file.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function
def main():
tree = astor.code_to_ast.parse_file("test_file.py")
print("DUMP TREE")
print(astor.dump_tree(tree))
print("SOURCE")
print(astor.to_source(tree))
#
# MAIN
#
if __name__ == '__main__':
main()
输出
$ python process_file.py
DUMP TREE
Module(
body=[
Expr(value=Call(func=Name(id='my_func'), args=[], keywords=[], starargs=None, kwargs=None))])
SOURCE
my_func()