Analyze the import dependency structure of a Python project.
Project description
depagon
depagon is a Python CLI tool and library that analyzes the import dependency structure of a Python project. It parses every .py file using the standard-library ast module, builds a directed graph of internal module dependencies, and reports:
- Circular imports — cycles in the dependency graph.
- Unused imports — names imported but never referenced in the same file.
- Coupling metrics — fan-in (how many modules import a given module) and fan-out (how many modules it imports).
It works as both a command-line tool (depagon scan ...) and an importable library.
Install
pip install depagon
Requires Python 3.10 or later. The only runtime dependency is rich.
Usage
Tree output (default)
Renders a dependency tree, coupling table, and findings in the terminal:
depagon scan path/to/myproject
Mermaid diagram
depagon scan path/to/myproject --output mermaid
Outputs a graph TD Mermaid string that can be pasted into any Mermaid-compatible renderer (GitHub Markdown, Mermaid Live Editor, etc.).
Graphviz DOT
depagon scan path/to/myproject --output dot
Outputs a DOT string suitable for dot -Tpng -o graph.png.
Show unused imports
depagon scan path/to/myproject --unused
Unused-import findings are collected during every run but only displayed when --unused is passed.
CI integration — exit code on cycles
depagon scan path/to/myproject --detect-cycles
Exits with status 1 if any circular imports are found, 0 otherwise. Wire it into your CI pipeline:
# GitHub Actions example
- run: depagon scan src/ --detect-cycles
Library usage
from depagon import Analyzer
from depagon.renderers import render_mermaid
result = Analyzer().analyze(Path("src/"))
# Inspect findings
for finding in result.findings:
print(finding.kind, finding.location, finding.detail)
# Render
print(render_mermaid(result))
Public API:
| Symbol | Module |
|---|---|
Scanner, ImportInfo, ModuleFile |
depagon.scanner |
DependencyGraph |
depagon.graph |
Analyzer, AnalysisResult, Finding |
depagon.analyzer |
render_tree, render_mermaid, render_dot |
depagon.renderers |
All of the above are also re-exported from depagon directly.
Skipped directories
The scanner automatically skips the following directory names to avoid scanning virtual environments and build artifacts:
__pycache__ venv .venv env .env envs virtualenv .virtualenv
node_modules .tox .mypy_cache dist build .pytest_cache .eggs site-packages
Any directory whose name starts with . (hidden directories) is also skipped.
If your environment uses a different name (e.g. my_env, .project_venv, py310), you can exclude it by subclassing Scanner and overriding _SKIP_DIRS:
from depagon.scanner import Scanner, _SKIP_DIRS
class MyScanner(Scanner):
pass
# Add your custom env name to the skip set
import depagon.scanner as _s
_s._SKIP_DIRS = _SKIP_DIRS | {"my_env", "py310", ".project_venv"}
Or build the graph programmatically and pass a custom scanner to the analyzer:
from pathlib import Path
import depagon.scanner as scanner_mod
from depagon.scanner import Scanner, _SKIP_DIRS
from depagon.analyzer import Analyzer
# Extend the skip set before scanning
scanner_mod._SKIP_DIRS = _SKIP_DIRS | {"my_env"}
result = Analyzer().analyze(Path("src/"))
How it works
-
Scanning —
Scanner.scan(root)walks the directory tree (skipping the directories listed above), reads each.pyfile, and parses it withast.parse. Files with syntax errors are skipped with a warning. Everyimportandfrom ... importstatement is recorded as anImportInfo. -
Graph construction —
Analyzer.analyze(root)resolves each import to its absolute dotted module name, checks whether it refers to a file inside the scanned directory (internal module), and adds a directed edge to aDependencyGraph. -
Cycle detection —
DependencyGraph.find_cycles()uses a colored DFS (white / gray / black) with a recursion stack. When a gray (in-progress) node is encountered again, the slice of the current path forms a cycle. Duplicate cycles are deduplicated via a frozenset key. -
Unused-import detection — For each file,
ast.walkcollects everyast.Nameidentifier. Any bound import name absent from this set is flagged. This is a conservative heuristic:import osis considered used ifosappears anywhere as an identifier in the file. -
Rendering — Three renderers produce different output formats from the same
AnalysisResult.
Building and publishing
Build a distribution:
pip install build
python -m build
This creates dist/depagon-0.1.0-py3-none-any.whl and a source tarball.
Upload to PyPI (requires a PyPI account and twine):
pip install twine
twine upload dist/*
Running tests
pip install pytest
pytest tests/ -v
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file depagon-0.1.0.tar.gz.
File metadata
- Download URL: depagon-0.1.0.tar.gz
- Upload date:
- Size: 104.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fceddbe874e5a11541f6527987c916e97f69e9da21b9ece637a53352e72531fc
|
|
| MD5 |
4232427867cb701d39bbb02fb9b2a4dc
|
|
| BLAKE2b-256 |
e5420e648add906ac4766053f8c798e70788a767d04db1dc5ea2c4431cff8459
|
File details
Details for the file depagon-0.1.0-py3-none-any.whl.
File metadata
- Download URL: depagon-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
586cfcb008f7f275ab22ae747de5f758e9b62e3caa427f761bb1e6e9f1e04731
|
|
| MD5 |
1026868a04c237d7547a5b6502a9ab9c
|
|
| BLAKE2b-256 |
7aedee3cf116a634ea4a4c8ba785bb903763a0cf2f980f26323735816f46bc36
|