玩弄ast
and compile
(内置)似乎您可以使用NodeTransformer
来修改某些节点...如果您知道要查找的内容,也可以手动编辑它们。
测试.py
print 'Dumb Guy'
x = 4 + 4
print x * 3
改变.py
import ast
with open('test.py') as f:
expr = f.read()
e = ast.parse(expr)
e.body[0].values[0].s = 'Cool Guy' # Replace the string
e.body[1].targets[0].id = 'herring' # Change x to herring
e.body[2].values[0].left.id = 'herring' # Change reference to x to reference to herring
c = compile(e, '<string>', 'exec')
exec(c)
change.py 的输出:
酷家伙
24
您也可以通过这种方式将代码添加到正文中(或以通常替换列表元素的方式替换元素):
p = ast.parse('print "Sweet!"', mode='single')
e.body.extend(p)
然后重新编译并执行:
c = compile(e, '<string>', 'exec')
exec(c)
您可以通过这种方式替换函数定义或单行。函数定义将有自己的主体,因此如果您添加了一些函数(或循环),您可以使用
e.body[N].body # Replace N with the index of the FunctionDef object
但是,我知道执行单个 ast 对象(_ast.Print
或_ast.Assign
其他对象)的唯一方法是执行以下操作:
e2 = ast.parse('', mode='exec')
e2.body.append(e.body[0])
exec(compile(e2, '<string>', 'exec'))
这对我来说似乎有点骇人听闻。就行而言 - AST 中的每个对象都有一个lineno
属性,因此如果您可以从异常中检索行号,您可以很容易地找出哪个语句引发了异常。
当然,这并不能真正解决将堆栈回退到异常前状态的问题,这听起来是你真正想做的事情。但是,可以通过pdb来做这样的事情。