Pyan
The Python static call graph generator. pyan3 on PyPI. Official development repo.
Install / Use
/learn @Technologicat/PyanREADME
Pyan3
Offline call graph generator for Python 3
For my stance on AI contributions, see the collaboration guidelines.
We use semantic versioning.
Pyan takes one or more Python source files, performs a (rather superficial) static analysis, and constructs a directed graph of the objects in the combined source, and how they define or use each other. The graph can be output for rendering by GraphViz or yEd, or as a plain-text dependency list.
This project has 2 official repositories:
- The original stable davidfraser/pyan.
- The development repository Technologicat/pyan
The PyPI package pyan3 is built from development
Note
The static analysis approach Pyan takes is different from running the code and seeing which functions are called and how often. There are various tools that will generate a call graph that way, usually using a debugger or profiling trace hooks, such as Python Call Graph.
Instead, Pyan reads through the source code, and makes deductions from its structure.
Revived! [February 2026]
Pyan3 is back in development. The analyzer has been modernized and tested on Python 3.10–3.14, with fixes for all modern syntax (walrus operator, match statements, async with, type aliases, and more). The plan is to keep Pyan3 up to date with new language releases.
What's new in the revival:
- Full support for Python 3.10–3.14 syntax
- Module-level import dependency analysis (
--module-levelflag andcreate_modulegraph()API), with import cycle detection - Graph depth control (
--depth), directional filtering (--direction), call path listing (--paths-from/--paths-to) - Comprehensive test suite (200+ tests, 91% branch coverage)
- Modernized build system and dependencies
This revival was carried out by Technologicat with Claude (Anthropic) as AI pair programmer. See AUTHORS.md for the full contributor history.
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->Table of Contents
<!-- markdown-toc end -->Overview
<!-- To regenerate graph0: pyan3 tests/orbital/*.py --dot --colored --no-defines --concentrate --file graph0.dot dot -Tsvg graph0.dot -o graph0.svg dot -Tpng graph0.dot -o graph0.png rm graph0.dot -->This example was rendered with the recommended options: --colored --no-defines --concentrate.
Uses relations are drawn with black solid arrows. Recursion is indicated by an arrow from a node to itself. Mutual recursion between nodes X and Y is indicated by a pair of arrows, one pointing from X to Y, and the other from Y to X. With --concentrate, bidirectional edges are merged into double-headed arrows.
Defines relations (drawn with dotted gray arrows) can be enabled with --defines.
Nodes are always filled, and made translucent to clearly show any arrows passing underneath them. This is especially useful for large graphs with GraphViz's fdp filter. If colored output is not enabled, the fill is white.
In node coloring, the HSL color model is used. The hue is determined by the filename the node comes from. The lightness is determined by depth of namespace nesting, with darker meaning more deeply nested. Saturation is constant. The spacing between different hues depends on the number of files analyzed; better results are obtained for fewer files.
Groups can be enabled with --grouped (and --nested-groups for nested subgraph clusters). Groups are filled with translucent gray to avoid clashes with any node color.
The nodes can be annotated by filename and source line number information.
Usage
Both CLI and Python API modes are available.
CLI usage
See pyan3 --help.
Basic examples:
# Generate DOT, then render with GraphViz
pyan3 *.py --uses --no-defines --colored --grouped --annotated --dot >myuses.dot
dot -Tsvg myuses.dot >myuses.svg
# Pass a directory — auto-globs **/*.py
pyan3 src/ --dot --colored --grouped >project.dot
# Generate SVG / HTML directly
pyan3 *.py --uses --no-defines --colored --grouped --annotated --svg >myuses.svg
pyan3 *.py --uses --no-defines --colored --grouped --annotated --html >myuses.html
# Output plain text — especially useful for feeding call graph info to coding AI agents
pyan3 src/ --uses --no-defines --text
Recommended options
For a clean uses-only call graph:
pyan3 src/*.py --dot --colored --no-defines --concentrate --file output.dot
dot -Tsvg output.dot -o output.svg
This omits defines edges (which tend to clutter the graph) and merges bidirectional uses edges into double-headed arrows. The dot layout works well for hierarchical call graphs; for larger graphs, fdp (force-directed) can produce more readable results:
pyan3 src/*.py --dot --colored --no-defines --concentrate --graphviz-layout fdp --file output.dot
fdp -Tsvg output.dot -o output.svg
For a high-level overview, add --depth 1 to collapse everything down to modules, classes, and top-level functions:
pyan3 src/*.py --dot --colored --no-defines --concentrate --depth 1 --file overview.dot
Graph depth control
Collapse the graph to a desired level of detail:
pyan3 src/ --dot --depth 0 # modules only (call-graph view, not import deps)
pyan3 src/ --dot --depth 1 # + classes and top-level functions
pyan3 src/ --dot --depth 2 # + methods
pyan3 src/ --dot --depth max # full detail (default)
Filtering
Focus on a specific function or namespace:
pyan3 src/ --dot --function pkg.mod.MyClass.method
pyan3 src/ --dot --namespace pkg.mod
# Control traversal direction (requires --function or --namespace)
pyan3 src/ --dot --function pkg.mod.func --direction down # callees only (what does this function call?)
pyan3 src/ --dot --function pkg.mod.func --direction up # callers only (what calls this function?)
Call path listing
List all call paths between two functions:
pyan3 src/ --paths-from pkg.mod.caller --paths-to pkg.mod.target
Uses depth-first search (DFS); results are sorted shortest first among those found, capped by --max-paths (default 100).
GraphViz layout options
pyan3 src/ --dot --graphviz-layout fdp # force-directed layout (also: neato, sfdp, twopi, circo)
pyan3 src/ --dot --dot-ranksep 1.5 # increase rank separation (inches)
pyan3 src/ --dot --concentrate # merge bidirectional edges into double-headed arrows
Note on --concentrate: GraphViz's edge concentration can produce small gaps at edge split/merge points (endpoint coordinates differ by ~0.02–0.09 graph units). This is a known GraphViz precision issue, visible at high zoom in interactive viewers. The visual output is still useful — just be aware that concentrated edges may not join perfectly.
Python API
import pyan
# Generate a call graph as a DOT string
dot_source = pyan.create_callgraph(
filenames="pkg/**/*.py", # also accepts a directory path
format="dot", # also: "svg", "html", "tgf", "yed", "text"
colored=True,
nested_groups=True,
draw_defines=True,
draw_uses=True,
depth=2, # 0=modules, 1=+classes, 2=+methods, None=full
direction="both", # "down" (callees), "up" (callers), "both"
concentrate=True, # merge bidirectional edges
layout="dot", # GraphViz layout algorithm
ranksep="0.5", # rank separation (inches)
)
# Find
Related Skills
node-connect
335.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
claude-opus-4-5-migration
82.5kMigrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5
frontend-design
82.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
model-usage
335.2kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.

