Find Dead Python Code with the AST Module
Learn how to use Python's built-in ast module to detect unused functions and variables in your codebase. A practical guide to building a lightweight dead code detector with no external dependencies.
How to Use Python's ast Module to Find Dead Code
We’ve all been there. You inherit a Python project with hundreds of files, and somewhere in that codebase are functions that haven’t been called in years. Variables assigned but never read. Imports that nobody uses. Dead code. It slows down maintenance, confuses new developers, and wastes your time during debugging.
Instead of manually scanning each file, you can automate the detection using Python’s built-in ast module. It’s lightweight, doesn’t require any external dependencies, and it’s already in your standard library.
Why ast Works Well for This
The ast module parses Python source code into an abstract syntax tree—a tree of nodes representing every statement, expression, and assignment. By walking this tree, we can track:
- Function definitions
- Variable assignments
- Calls to functions or methods
- Imports
The core idea is simple: if a function is defined but never called, it’s dead. If a variable is assigned but never read, it’s also dead. Let’s build a small detector step by step.
Writing a Basic Dead Code Detector
I’ll walk you through a script that finds unused functions in a single file. Start by importing ast and defining a visitor class.
import ast
class DeadCodeFinder(ast.NodeVisitor):
def __init__(self):
self.functions = {} # name -> node (for definition)
self.calls = set() # names of called functions
self.unused_functions = []
def visit_FunctionDef(self, node):
self.functions[node.name] = node
self.generic_visit(node) # continue into the function body
def visit_Call(self, node):
if isinstance(node.func, ast.Name):
self.calls.add(node.func.id)
self.generic_visit(node)
def report(self):
for name, node in self.functions.items():
if name not in self.calls:
self.unused_functions.append(name)
return self.unused_functions
This visitor does two things:
1. Records every function definition.
2. Records every function call (assuming it’s a simple Name call, not something like obj.method()).
After visiting the whole file, we compare definitions against calls.
Running It on a Sample File
Let’s say we have a file called example.py:
def used_function():
print("I'm used")
def dead_function():
print("I'm never called")
def another_dead():
return 42
used_function()
Now run our detector:
with open("example.py", "r") as f:
tree = ast.parse(f.read())
finder = DeadCodeFinder()
finder.visit(tree)
print(finder.report())
Output:
['dead_function', 'another_dead']
Exactly what we expected. The detector correctly ignores used_function because it sees the call.
Limitations and How to Improve
The above script is intentionally simple. In a real-world PythonSkillset codebase, you’ll face trickier cases:
- Imported functions: If you call
math.sqrt, the function name in the AST issqrt, notmath.sqrt. You’ll need to track import aliases. - Methods and attributes:
obj.method()appears as anAttributenode, not aName. You need to handle those differently. - Dynamic calls:
getattr(obj, 'method')()orfunc = some_lookup['key']; func()won’t be caught by a static analyzer.
For a more robust tool, you can analyze all files in a project using os.walk and build a cross-file call graph. Some dedicated tools like vulture do exactly this, but building your own gives you flexibility and a deeper understanding of how Python code is structured.
When to Use This in Practice
Dead code detection isn’t a one-time thing. I recommend running a script like this:
- Before major refactors
- When cleaning up long-running projects
- After merging large feature branches
It’s also a great teaching tool for PythonSkillset readers who want to understand AST traversal without diving into compiler theory.
Final thought: The ast module is one of Python’s hidden gems. By walking the syntax tree, you can build linters, minifiers, code formatters, and yes—dead code detectors. Start small, handle edge cases incrementally, and you’ll have a tool that saves you hours of manual cleanup.
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.