Fast intra-procedural control flow graph generation for Python.
No runtime required. Text, JSON, and DOT output for humans, machines, and agents.
$ git clone https://github.com/nwyin/pycfg-rs && cd pycfg-rs $ cargo build --release $ target/release/pycfg src/app/service.py::UserService.get_profile
Python source
def process(request):
try:
user = authenticate(request)
if user.is_admin:
return admin_dashboard(user)
return user_dashboard(user)
except AuthError:
return redirect("/login")
finally:
log_access(request)
What pycfg-rs produces
$ pycfg src/app/service.py::process
Function: process (line 1)
Block 0 (entry):
[L2] try:
-> 1 (try-body)
-> 3 (exception: AuthError)
Block 1 (try-body):
[L3] user = authenticate(request)
[L4] if user.is_admin:
-> 2 (True)
-> 5 (False)
Block 2 (if-true):
[L5] return admin_dashboard(user)
-> 4 (finally)
Block 3 (except: AuthError):
[L7] return redirect("/login")
-> 4 (finally)
Block 4 (finally):
[L9] log_access(request)
-> 6 (exit)
Block 5 (if-false):
[L6] return user_dashboard(user)
-> 4 (finally)
Block 6 (exit):
Metrics: 7 blocks, 8 edges, 2 branches, CC=4
Speed measured on the rich corpus (100 files, 984 functions). * python-graphs only analyzed 13% of files due to import requirements. Roadmap and limitations.
| pycfg-rs | staticfg | python-graphs | |
|---|---|---|---|
| Speed (rich, 100 files) | 65 ms | 84 ms | 7 ms* |
| Per-function speed | 0.066 ms | 0.35 ms | 0.19 ms |
| File coverage | 100% | 74% | 13% |
| Output formats | text / JSON / DOT | DOT only | Python object |
| Python 3.10+ (match/case) | yes | no | no |
| Runtime required | no | no | yes (import) |
| Language | Rust | Python | Python |
| Maintained | active | unmaintained | archived |
For inter-procedural call graphs, see pycg-rs — together they cover call graphs and control flow for Python static analysis.
Analysis of 9 popular open-source Python projects, run on every push. Expand a project to see complexity distribution and CFG visualizations.