I'm unfamiliar with the AST module and would appreciate any insight. If, for example, I have a string that contains a valid python script such as
import sys #Just any module
class SomeClass:
def __init__(self):
self.x = 10
self.b = 15
def a_func(self):
print(self.x)
I would like to be able to programmatically edit lines such as changing self.x = 10 to something like self.x = 20. I can break it down somewhat with ast via:
some_string = "..." #String of class above
for body_item in ast.parse(some_string):
...
But this doesn't feel like the "right" way(not that there is a right way since this is somewhat niche). I was hoping someone could correct me towards something cleaner, or just better.
You can start by using ast.dump to get an idea of the AST structure of the code you're dealing with:
import ast
code='self.x = 10'
print(ast.dump(ast.parse(code), indent=2))
This outputs:
Module(
body=[
Assign(
targets=[
Attribute(
value=Name(id='self', ctx=Load()),
attr='x',
ctx=Store())],
value=Constant(value=10))],
type_ignores=[])
From which you can see what you want to look for is an Assign node where the first of targets is an Attribute node whose value is a Name node with an id of 'self' and an attr of 'x'.
With this knowledge, you can then use ast.walk to traverse the AST nodes to look for a node with the aforementioned properties, modify its value to a Constant node with a value of 20, and finally use ast.unparse to convert AST back to a string of code:
import ast
code = '''
import sys #Just any module
class SomeClass:
def __init__(self):
self.x = 10
self.b = 15
def a_func(self):
print(self.x)
'''
tree = ast.parse(code)
for node in ast.walk(tree):
if (
isinstance(node, ast.Assign) and
isinstance((target := node.targets[0]), ast.Attribute) and
isinstance(target.value, ast.Name) and
target.value.id == 'self' and
target.attr == 'x'
):
node.value = ast.Constant(value=20)
print(ast.unparse(tree))
This outputs:
class SomeClass:
def __init__(self):
self.x = 20
self.b = 15
def a_func(self):
print(self.x)
Note that ast.unparse requires Python 3.10 or later. If you're using an earlier version, you can use astunparse.unparse from the astunparse package instead.
Demo: https://trinket.io/python3/3b09901326
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With