Static analysis tool for tracing exception flow through Python codebases
Project description
flow
Static analysis tool for tracing exception flow through Python codebases.
What can escape from my API endpoints? Flow answers this by parsing your code, building a call graph, and computing which exceptions propagate to each entrypoint.
Quick Start
pip install flow-analysis
# Check Flask routes for uncaught exceptions
flow flask audit -d /path/to/project
# Check FastAPI routes
flow fastapi audit -d /path/to/project
# Deep dive into one function
flow escapes create_user -d /path/to/project
# Visualize the call tree
flow trace create_user -d /path/to/project
What It Does
Flow finds your HTTP routes and CLI scripts, traces the call graph, and reports which exceptions can escape:
$ flow flask audit
Scanning 23 flask entrypoints...
3 entrypoints have uncaught exceptions:
POST /users/import
└─ FileNotFoundError (importers.py:45)
└─ ValidationError (validators.py:12)
GET /reports/{id}
└─ PermissionError (auth.py:89)
20 entrypoints fully covered by exception handlers
For a specific endpoint, see the full picture:
$ flow escapes create_user
Exceptions that can escape from POST /users:
FRAMEWORK-HANDLED (converted to HTTP response):
HTTPException
└─ becomes: HTTP 404
└─ raised in: routes/users.py:45 (get_user) [high confidence]
CAUGHT BY GLOBAL HANDLER:
ValidationError (@errorhandler(AppError))
└─ raised in: validators.py:27 (validate_input) [high confidence]
UNCAUGHT (will propagate to caller):
ConnectionError
└─ raised in: db/client.py:45 (execute) [medium confidence]
└─ call path: create_user → save_user → db.execute
Visualize as a tree:
$ flow trace create_user
POST /users → escapes: ValidationError, ConnectionError
├── validate_input() → ValidationError
│ └── raises ValidationError (validators.py:27)
└── save_user() → ConnectionError
└── db.execute() → ConnectionError
└── raises ConnectionError (db/client.py:45)
Features
- Entrypoint detection: Flask routes, FastAPI routes, CLI scripts (
if __name__ == "__main__") - Global handler awareness: Understands
@errorhandler,add_exception_handler - Exception hierarchy: Knows that catching
AppErroralso catchesValidationErrorif it's a subclass - Polymorphism: Expands abstract method calls to all concrete implementations
- Framework-handled exceptions: Detects HTTPException, ValidationError → HTTP responses
- Confidence levels: Shows high/medium/low confidence based on resolution quality
- Resolution modes:
--strictfor precision,--aggressivefor recall - Exception stubs: Declare what external libraries can raise (requests, sqlalchemy, etc.)
- JSON output: All commands support
-f jsonfor CI/automation - Caching: SQLite-based caching for fast repeated analysis
Commands
Core Commands (framework-agnostic)
| Command | Description |
|---|---|
flow raises <exception> |
Find all places an exception is raised |
flow escapes <function> |
Show what can escape from a specific function |
flow callers <function> |
Find all callers of a function |
flow catches <exception> |
Find all places an exception is caught |
flow trace <function> |
Visualize exception flow as a call tree |
flow exceptions |
Show the exception class hierarchy |
flow subclasses <class> |
Show class inheritance tree |
flow stubs <action> |
Manage exception stubs (list, init, validate) |
flow stats |
Show codebase statistics |
Framework-Specific Commands
| Command | Description |
|---|---|
flow flask audit |
Check Flask routes for escaping exceptions |
flow flask entrypoints |
List Flask HTTP routes |
flow flask routes-to <exc> |
Which Flask routes can trigger this exception? |
flow fastapi audit |
Check FastAPI routes for escaping exceptions |
flow fastapi entrypoints |
List FastAPI HTTP routes |
flow fastapi routes-to <exc> |
Which FastAPI routes can trigger this exception? |
flow cli audit |
Check CLI scripts for escaping exceptions |
flow cli entrypoints |
List CLI scripts |
flow cli scripts-to <exc> |
Which CLI scripts can trigger this exception? |
All commands accept:
-d, --directory: Directory to analyze (default: current)-f, --format: Output format (textorjson)--no-cache: Disable caching
The escapes command accepts additional flags:
--strict: High precision mode - only includes precisely resolved calls--aggressive: High recall mode - includes fuzzy matches
Supported Frameworks
Detected automatically:
- Flask (
@app.route,@blueprint.route,@app.errorhandler) - FastAPI (
@router.get/post/put/delete,add_exception_handler) - CLI scripts (
if __name__ == "__main__")
Not yet supported:
- Django
- Celery tasks
- Scheduled jobs (APScheduler, etc.)
Custom patterns can be added via .flow/detectors/ (run flow init to set up).
Adding Custom Detectors
Flow is designed to be extended with AI coding agents. The detector interface is intentionally simple: implement a protocol that returns entrypoints and handlers from parsed code.
To add support for a new framework (Django, Celery, your internal RPC layer, etc.):
- Run
flow initto create the.flow/directory structure - Point your AI agent at
flow/protocols.pyto see theEntrypointDetectorinterface - Ask it to implement a detector for your framework in
.flow/detectors/
Example prompt for an AI agent:
Read flow/protocols.py and flow/detectors.py to understand how entrypoint
detection works. Then implement a detector for Django that finds:
- Views decorated with @api_view
- Class-based views inheriting from APIView
- URL patterns in urls.py
Put the implementation in .flow/detectors/django.py
The detector just needs to implement:
detect_entrypoints(functions, classes, ...)→ list ofEntrypointdetect_global_handlers(...)→ list ofGlobalHandler
Flow will automatically load any .py files in .flow/detectors/ and use them alongside the built-in Flask/FastAPI detectors.
Configuration
Flow can be configured via .flow/config.yaml:
resolution_mode: default # "strict", "default", or "aggressive"
exclude:
- vendor
- migrations
Exception Stubs
Flow includes built-in stubs for common libraries (requests, sqlalchemy, httpx, redis, boto3). These declare what exceptions external library functions can raise.
Add custom stubs in .flow/stubs/:
# .flow/stubs/mylib.yaml
mylib:
do_thing:
- MyLibError
- TimeoutError
Manage stubs with flow stubs list and flow stubs validate.
How It Works
- Parse: LibCST parses all Python files
- Extract: Find functions, classes, raise/catch sites, calls, entrypoints
- Build call graph: Track who calls whom, resolve method calls
- Propagate: Fixed-point iteration computes which exceptions escape each function
- Report: For each entrypoint, show caught vs uncaught exceptions
Limitations
- Over-approximation: May report more exceptions than actually possible (e.g., all implementations of an abstract method)
- Under-approximation: Dynamic dispatch,
eval(), and external libraries can't be fully traced - No runtime info: Analysis is purely static
Development
git clone https://github.com/ianm199/flow
cd flow
pip install -e ".[dev]"
pytest
License
MIT
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 flow_analysis-0.2.0.tar.gz.
File metadata
- Download URL: flow_analysis-0.2.0.tar.gz
- Upload date:
- Size: 55.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0f94fb21d96a407ce035377855c054c341124d3f9a98e1271d69c9f855d5079
|
|
| MD5 |
82c00591439d659f32dea5d13d58a615
|
|
| BLAKE2b-256 |
3e48705232057b2473ca808f3494c831978beeefd3e612f25296db7ecdbf1e2a
|
File details
Details for the file flow_analysis-0.2.0-py3-none-any.whl.
File metadata
- Download URL: flow_analysis-0.2.0-py3-none-any.whl
- Upload date:
- Size: 60.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0b3708854123dd7ee064586f3925723a1badcc1d400596568397e6bb1b721fa
|
|
| MD5 |
ffff94757c68200a131d2f76d64986db
|
|
| BLAKE2b-256 |
bf8a2f109f08ef63276f81384c44ca489c737cb2da4365d2f0b2989d2222530c
|