Python’s modulefinder module traces all imports in a script (and their transitive imports) to produce a complete dependency list. from modulefinder import ModuleFinder. Create with custom path: mf = ModuleFinder(path=None) — path overrides sys.path; defaults to sys.path. Trace: mf.run_script("script.py") — loads and traces the script, recursively following all import statements. Results: mf.modules — dict[str, Module] of successfully located modules; mf.badmodules — dict[str, dict] of modules that were imported but not found; mf.any_missing() → list[str] of names in badmodules not present in modules. Module object: mod.__name__, mod.__file__ (path or None for built-ins), mod.__path__ (list for packages). Report: mf.report() — prints a tabular summary to stdout. Each Module is an instance of modulefinder.Module with a globalnames set of names used at module scope. Claude Code generates dependency graphs, freeze validators, missing-module detectors, and deployment manifests.
CLAUDE.md for modulefinder
## modulefinder Stack
- Stdlib: from modulefinder import ModuleFinder
- Trace: mf = ModuleFinder(); mf.run_script("main.py")
- Found: mf.modules # {name: Module} — all located modules
- Bad: mf.badmodules # {name: {}} — not found
- Missing: mf.any_missing() # list of unresolvable imports
- Report: mf.report() # print tabular summary
- Note: traces static imports; dynamic __import__ / importlib may be missed
modulefinder Dependency Finder Pipeline
# app/modulefinderutil.py — dep graph, missing detector, manifest, freeze checker
from __future__ import annotations
import sys
from dataclasses import dataclass, field
from modulefinder import ModuleFinder
from pathlib import Path
# ─────────────────────────────────────────────────────────────────────────────
# 1. Module info dataclass
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class ModuleRecord:
"""
Human-readable descriptor for one module in the dependency graph.
Fields:
name — dotted module name (e.g. "os.path")
path — absolute path to the source file, or None for C extensions / built-ins
is_pkg — True if the module is a package (has __path__)
is_builtin — True if no file is associated (C built-in)
missing — True if the module could not be located
"""
name: str
path: "str | None"
is_pkg: bool
is_builtin: bool
missing: bool
def __str__(self) -> str:
if self.missing:
return f"MISSING {self.name}"
kind = "pkg" if self.is_pkg else ("builtin" if self.is_builtin else "module")
loc = self.path or "<built-in>"
return f"{kind:8s} {self.name:40s} {loc}"
# ─────────────────────────────────────────────────────────────────────────────
# 2. Core trace function
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class DependencyGraph:
"""
Complete dependency analysis for a Python script.
Example:
graph = trace_script("scripts/deploy.py")
print(f"found={graph.found_count} missing={graph.missing_count}")
for rec in graph.missing_records:
print(f" MISSING: {rec.name}")
"""
script: Path
records: list[ModuleRecord] = field(default_factory=list)
_mf: "ModuleFinder | None" = field(default=None, repr=False)
@property
def found_records(self) -> list[ModuleRecord]:
return [r for r in self.records if not r.missing]
@property
def missing_records(self) -> list[ModuleRecord]:
return [r for r in self.records if r.missing]
@property
def found_count(self) -> int:
return len(self.found_records)
@property
def missing_count(self) -> int:
return len(self.missing_records)
@property
def third_party(self) -> list[ModuleRecord]:
"""Records that are not stdlib built-ins and not missing."""
stdlib_prefix = str(Path(sys.prefix))
result = []
for r in self.found_records:
if r.is_builtin:
continue
if r.path and r.path.startswith(stdlib_prefix):
continue
result.append(r)
return result
def source_files(self) -> list[Path]:
"""Return sorted list of .py source file paths for all found modules."""
paths = []
for r in self.found_records:
if r.path and r.path.endswith(".py"):
paths.append(Path(r.path))
return sorted(set(paths))
def trace_script(
script: "str | Path",
path: "list[str] | None" = None,
) -> DependencyGraph:
"""
Run modulefinder on script and return a DependencyGraph.
Example:
graph = trace_script("app/main.py")
for rec in graph.missing_records:
print(f" MISSING: {rec.name}")
"""
mf = ModuleFinder(path=path or sys.path[:])
try:
mf.run_script(str(script))
except SystemExit:
pass # some scripts call sys.exit() at top level
records: list[ModuleRecord] = []
for name, mod in sorted(mf.modules.items()):
file_path = getattr(mod, "__file__", None)
mod_path = getattr(mod, "__path__", None)
records.append(ModuleRecord(
name=name,
path=file_path,
is_pkg=bool(mod_path),
is_builtin=(file_path is None),
missing=False,
))
for name in sorted(mf.badmodules.keys()):
if name not in mf.modules:
records.append(ModuleRecord(
name=name,
path=None,
is_pkg=False,
is_builtin=False,
missing=True,
))
return DependencyGraph(script=Path(script), records=records, _mf=mf)
# ─────────────────────────────────────────────────────────────────────────────
# 3. Quick helpers
# ─────────────────────────────────────────────────────────────────────────────
def missing_modules(script: "str | Path") -> list[str]:
"""
Return sorted list of module names imported by script but not found.
Example:
missing = missing_modules("app/main.py")
if missing:
print("Missing:", missing)
"""
graph = trace_script(script)
return sorted(r.name for r in graph.missing_records)
def all_source_files(script: "str | Path") -> list[Path]:
"""
Return all .py source files transitively imported by script.
Example:
for p in all_source_files("main.py"):
print(p)
"""
return trace_script(script).source_files()
def is_self_contained(script: "str | Path") -> bool:
"""
Return True if the script has no missing imports.
Example:
if not is_self_contained("deploy.py"):
print("missing imports — will fail at runtime")
"""
return trace_script(script).missing_count == 0
# ─────────────────────────────────────────────────────────────────────────────
# 4. Deployment manifest builder
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class DeployManifest:
"""
Build a deployment manifest: list of source files needed to run a script.
Example:
manifest = DeployManifest.from_script("app/server.py")
manifest.write("dist/manifest.txt")
for path in manifest.files:
print(path)
"""
script: Path
files: list[Path] = field(default_factory=list)
missing: list[str] = field(default_factory=list)
@classmethod
def from_script(
cls,
script: "str | Path",
include_stdlib: bool = False,
) -> "DeployManifest":
graph = trace_script(script)
files: list[Path] = []
stdlib_prefix = str(Path(sys.prefix))
for r in graph.found_records:
if r.is_builtin:
continue
if r.path is None:
continue
if not include_stdlib and r.path.startswith(stdlib_prefix):
continue
if r.path.endswith(".py"):
files.append(Path(r.path))
return cls(
script=Path(script),
files=sorted(set(files)),
missing=sorted(r.name for r in graph.missing_records),
)
def write(self, dest: "str | Path") -> None:
"""Write manifest to a text file (one path per line)."""
lines = [str(self.script)] + [str(p) for p in self.files]
Path(dest).write_text("\n".join(lines) + "\n")
def print_report(self) -> None:
"""Print a human-readable manifest report."""
print(f"Script: {self.script}")
print(f"Files: {len(self.files)}")
for p in self.files:
print(f" {p}")
if self.missing:
print(f"Missing: {len(self.missing)}")
for m in self.missing:
print(f" MISSING: {m}")
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import tempfile
print("=== modulefinder demo ===")
with tempfile.TemporaryDirectory() as td:
td_path = Path(td)
# ── write test scripts ─────────────────────────────────────────────────
helper = td_path / "helper.py"
helper.write_text(
"import os\nimport json\n\ndef get_info():\n return {'cwd': os.getcwd()}\n"
)
main = td_path / "main.py"
main.write_text(
"import helper\nimport hashlib\nimport nonexistent_lib\n\n"
"data = helper.get_info()\nprint(data)\n"
)
# ── trace_script ──────────────────────────────────────────────────────
print("\n--- trace_script ---")
graph = trace_script(main, path=[str(td_path)] + sys.path)
print(f" found={graph.found_count} missing={graph.missing_count}")
# ── found modules (non-missing) ───────────────────────────────────────
print("\n--- found modules ---")
for rec in sorted(graph.found_records, key=lambda r: r.name)[:8]:
print(f" {rec}")
# ── missing modules ────────────────────────────────────────────────────
print("\n--- missing modules ---")
for rec in graph.missing_records:
print(f" {rec}")
# ── is_self_contained ─────────────────────────────────────────────────
print("\n--- is_self_contained ---")
print(f" main.py: {is_self_contained.__name__} = "
f"{is_self_contained.__doc__ and 'checked'}")
graph2 = trace_script(main, path=[str(td_path)] + sys.path)
print(f" has no missing: {graph2.missing_count == 0}")
# ── source_files ──────────────────────────────────────────────────────
print("\n--- source_files ---")
sources = graph.source_files()
for p in sources[:5]:
print(f" {p}")
# ── DeployManifest ────────────────────────────────────────────────────
print("\n--- DeployManifest ---")
manifest = DeployManifest.from_script(main, include_stdlib=False)
manifest_path = td_path / "manifest.txt"
manifest.write(manifest_path)
print(f" manifest written to: {manifest_path.name}")
print(f" non-stdlib files: {len(manifest.files)}")
print(f" missing deps: {manifest.missing}")
print("\n=== done ===")
For the pip show / pipdeptree (PyPI) alternative — pipdeptree traces package-level dependencies from installed distributions, not import-level module references — use pipdeptree to understand distribution-level requirements for packaging; use modulefinder when you want to know exactly which .py files and stdlib modules a specific script or entry-point loads at import time, independent of how modules are installed (useful for freeze/bundle workflows). For the importlib.util.find_spec alternative — importlib.util.find_spec("module") checks whether a single module is locatable on sys.path without executing any code — use find_spec for fast single-module existence checks; use modulefinder when you need the complete transitive dependency graph of a script, including indirect imports through libraries. The Claude Skills 360 bundle includes modulefinder skill sets covering ModuleRecord with is_pkg/is_builtin/missing flags, DependencyGraph with found_records/missing_records/third_party/source_files() views, trace_script() ModuleFinder driver, missing_modules()/all_source_files()/is_self_contained() quick helpers, and DeployManifest.from_script() with write()/print_report() deployment manifest builder. Start with the free tier to try dependency tracing patterns and modulefinder pipeline code generation.