Skip to content

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:

Language: Python Filename: 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:

Language: Python
>>> 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 .badmodules mapping
  • Accepts an excludes list 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:

Language: Python
>>> 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:

Language: Python
>>> 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:

Language: Python
>>> 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:

Language: Shell
$ 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:

Language: Python Filename: 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:

Language: Shell
$ 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.

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.

intermediate python stdlib

For additional information on related topics, take a look at the following resources:


By Leodanis Pozo Ramos • Updated May 7, 2026