Python AST Basics and Practical Use for Automated Variable Renaming and Logging
This article explains Python's compilation process, introduces the AST module, and demonstrates how to parse, pretty‑print, traverse, and transform abstract syntax trees to automatically rename ambiguous variables and inject logging statements, ending with a discussion of broader AST applications.
When a team lacks naming conventions or code‑review mechanisms, ambiguous variable names such as a, b, c degrade readability; manually renaming them is error‑prone, so a fast and accurate method is needed.
Python, like Java, is interpreted by CPython which first compiles source code into bytecode and then executes it on a virtual machine, making the compilation process a six‑stage pipeline that can be inspected with the py_compile module and the ast module.
The official ast documentation (https://docs.python.org/3/library/ast.html#ast-helpers) describes the module as a helper for processing Python abstract syntax trees, noting that the grammar may change between releases and that trees can be generated via compile(..., ast.PyCF_ONLY_AST) or ast.parse().
Using the astpretty library, the source file web_util.py can be parsed and its AST printed in a readable form:
# -*- encoding: utf-8 -*-
class opUtil:
@staticmethod
def add_two_num(a, b):
return a + b
@staticmethod
def mul_two_num(a, b):
print(a, b)
return a * bTo rename the parameter a to a more meaningful name and insert logging, a custom ast.NodeTransformer subclass is used. The transformer checks for nodes with id or arg equal to "a" and replaces them with "first_num", while also injecting a print statement at the start of each function:
# -*- encoding: utf-8 -*-
import ast, astunparse
import astpretty
filename = "web_util.py"
f = open(filename)
ast_obj = ast.parse(f.read(), mode="exec")
astpretty.pprint(ast_obj)
class visitor_ast(ast.NodeTransformer):
def generic_visit(self, node):
print("ALL", type(node).__name__)
fields = node._fields
if "id" in fields and node.id == "a":
print("field id", node.id)
node.id = "first_num"
ast.NodeVisitor.generic_visit(self, node)
def visit_FunctionDef(self, node):
ast.NodeVisitor.generic_visit(self, node)
args_num = len(node.args.args)
args = tuple([arg.arg for arg in node.args.args])
func_log_stmt = ''.join(["print('calling func: %s', " % node.name, "'args:'", ", %s" * args_num % args, ")"])
node.body.insert(0, ast.parse(func_log_stmt))
def visit_arg(self, node):
fields = node._fields
if "arg" in fields and node.arg == "a":
print("field arg", node.arg)
node.arg = "first_num"
print("ARG", type(node).__name__, node.arg)
ast.NodeVisitor.generic_visit(self, node)
v = visitor_ast()
v.visit(ast_obj)
astpretty.pprint(ast_obj)
print(astunparse.unparse(ast_obj))After transformation, the regenerated source code shows the renamed parameters and added logging:
class opUtil():
@staticmethod
def add_two_num(first_num, b):
print('calling func: add_two_num', 'args:', first_num, b)
return (first_num + b)
@staticmethod
def mul_two_num(first_num, b):
print('calling func: mul_two_num', 'args:', first_num, b)
print(first_num, b)
return (first_num * b)In summary, although AST is rarely used directly in everyday business development, it is valuable for tasks such as global variable renaming, automatic logging insertion, syntax checking, detecting Chinese characters, closure analysis, and more; future articles will explore additional use cases.
360 Quality & Efficiency
360 Quality & Efficiency focuses on seamlessly integrating quality and efficiency in R&D, sharing 360’s internal best practices with industry peers to foster collaboration among Chinese enterprises and drive greater efficiency value.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
