modulefinder
The Python modulefinder module determines the set of modules imported by a Python script by statically analyzing its code and following every import statement it finds, including the ones reached through the modules and packages that those first imports pull in. It is mainly used by packaging and freezing tools that need to know the full dependency graph of a program without actually running it.
For example, suppose you have a small reporting script that pulls in a few standard library helpers:
report.py
import csv
import json
from pathlib import Path
from collections import Counter
# Report generation code goes here...
You can scan the file with ModuleFinder to see which modules it imports, without ever executing the script:
>>> from modulefinder import ModuleFinder
>>> finder = ModuleFinder()
>>> finder.run_script("report.py")
>>> finder.modules["csv"]
Module('csv', '.../csv.py')
The .modules attribute maps each discovered dotted name to the corresponding module object, while .badmodules collects any names the finder couldn’t statically resolve.
Key Features
- Walks a script and every module it imports without executing the code
- Collects resolved modules in a dictionary keyed by dotted module name
- Reports unresolved or conditional imports in a separate
.badmodulesmapping - Accepts an
excludeslist to skip modules that should not be followed - Lets callers register an extra search path for a package with
AddPackagePath() - Supports module aliasing through
ReplacePackage() - Can be invoked as a script to print a dependency report for any Python file
Frequently Used Classes and Functions
| Object | Type | Description |
|---|---|---|
modulefinder.ModuleFinder |
Class | Analyzes a script and records all modules it imports |
ModuleFinder.run_script() |
Method | Parses the given file and follows its imports |
ModuleFinder.report() |
Method | Prints a summary of found and missing modules to standard output |
ModuleFinder.modules |
Attribute | Maps each discovered dotted name to a Module object |
modulefinder.AddPackagePath() |
Function | Registers an extra directory to search for a given package |
modulefinder.ReplacePackage() |
Function | Declares that one module name should be treated as another |
Examples
Reusing the report.py script from earlier, you can look up any resolved module by name:
>>> from modulefinder import ModuleFinder
>>> finder = ModuleFinder()
>>> finder.run_script("report.py")
>>> finder.modules["json"]
Module('json', '.../json/__init__.py', ['.../json'])
By default, ModuleFinder follows every transitive import. The re module shows up in the graph because csv and json both import it under the hood:
>>> from modulefinder import ModuleFinder
>>> finder = ModuleFinder()
>>> finder.run_script("report.py")
>>> "re" in finder.modules
True
Adding a module to excludes keeps it out of the graph:
>>> from modulefinder import ModuleFinder
>>> finder = ModuleFinder(excludes=["re"])
>>> finder.run_script("report.py")
>>> "re" in finder.modules
False
Running modulefinder directly from the command line to print a full report:
$ python -m modulefinder report.py
Name File
---- ----
m __main__ report.py
P collections .../collections/__init__.py
m csv .../csv.py
P json .../json/__init__.py
P pathlib .../pathlib/__init__.py
...
Common Use Cases
The most common tasks for modulefinder include:
- Inspecting which standard-library and third-party modules a script pulls in
- Building the dependency graph that freezing tools use to bundle a script into a single executable
- Detecting broken or conditional imports before shipping a program
- Auditing legacy code to find unused or unexpected dependencies
- Teaching how Python resolves module names during import
Real-World Example
Packaging tools use modulefinder to figure out what to bundle. The audit script below takes a script’s path as a command-line argument. It prints a sample of top-level dependencies and flags any unresolved imports, giving you a preview of what a packager will pull in:
audit_imports.py
import sys
from modulefinder import ModuleFinder
def main(path):
finder = ModuleFinder()
finder.run_script(path)
top_level = sorted(
name for name in finder.modules
if "." not in name and not name.startswith("_")
)
print("Top-level modules:", len(top_level))
for name in top_level[:10]:
print(" -", name)
user_missing = sorted(
name for name, refs in finder.badmodules.items()
if "__main__" in refs
)
if user_missing:
print("Missing:", ", ".join(user_missing))
if __name__ == "__main__":
main(sys.argv[1])
Running the audit against report.py produces a compact summary:
$ python audit_imports.py report.py
Top-level modules: 138
- abc
- annotationlib
- argparse
- array
- ast
- asyncio
- atexit
- base64
- bdb
- binascii
Missing: collections.Counter, pathlib.Path
Even a four-import script pulls in well over a hundred standard-library modules transitively. The Missing line shows that modulefinder treats from X import Y statements as X.Y submodule lookups, so class or function imports, like Counter and Path, surface as unresolved names alongside any genuine typos or missing third-party dependencies.
Related Resources
Tutorial
Python import: Advanced Techniques and Tips
The Python import system is as powerful as it is useful. In this in-depth tutorial, you'll learn how to harness this power to improve the structure and maintainability of your code.
For additional information on related topics, take a look at the following resources:
- Python's __all__: Packages, Modules, and Wildcard Imports (Tutorial)
- Using PyInstaller to Easily Distribute Python Applications (Tutorial)
- Absolute vs Relative Imports in Python (Tutorial)
- What's a Python Namespace Package, and What's It For? (Tutorial)
- Python Zip Imports: Distribute Modules and Packages Quickly (Tutorial)
- uv vs pip: Managing Python Packages and Dependencies (Tutorial)
- Advanced Python import Techniques (Course)
- Python import: Advanced Techniques and Tips (Quiz)
- Python's __all__: Packages, Modules, and Wildcard Imports (Quiz)
- Absolute vs Relative Imports in Python (Course)
- Absolute vs Relative Imports in Python (Quiz)
- Python Namespace Packages (Quiz)
- uv vs pip: Python Packaging and Dependency Management (Course)
- uv vs pip: Managing Python Packages and Dependencies (Quiz)
By Leodanis Pozo Ramos • Updated May 7, 2026