Skip to main content

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 AppError also catches ValidationError if 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: --strict for precision, --aggressive for recall
  • Exception stubs: Declare what external libraries can raise (requests, sqlalchemy, etc.)
  • JSON output: All commands support -f json for 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 (text or json)
  • --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.):

  1. Run flow init to create the .flow/ directory structure
  2. Point your AI agent at flow/protocols.py to see the EntrypointDetector interface
  3. 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 of Entrypoint
  • detect_global_handlers(...) → list of GlobalHandler

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

  1. Parse: LibCST parses all Python files
  2. Extract: Find functions, classes, raise/catch sites, calls, entrypoints
  3. Build call graph: Track who calls whom, resolve method calls
  4. Propagate: Fixed-point iteration computes which exceptions escape each function
  5. 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

Metadata

Release files for flow-analysis 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for flow-analysis 0.2.0
File Size Uploaded Provenance Actions
flow_analysis-0.2.0.tar.gz 55.7 kB Without provenance Details for flow_analysis-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for flow-analysis 0.2.0
File Interpreter ABI Platform Actions
flow_analysis-0.2.0-py3-none-any.whl Python 3 none any Details for flow_analysis-0.2.0-py3-none-any.whl

Total release size: 115.8 kB

Release files / flow_analysis-0.2.0.tar.gz

Download URL flow_analysis-0.2.0.tar.gz
Size 55.7 kB
Tags Source
SHA-256 checksum
How to use checksums
b0f94fb21d96a407ce035377855c054c341124d3f9a98e1271d69c9f855d5079
BLAKE2b-256 checksum
How to use checksums
3e48705232057b2473ca808f3494c831978beeefd3e612f25296db7ecdbf1e2a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Release files / flow_analysis-0.2.0-py3-none-any.whl

Download URL flow_analysis-0.2.0-py3-none-any.whl
Size 60.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c0b3708854123dd7ee064586f3925723a1badcc1d400596568397e6bb1b721fa
BLAKE2b-256 checksum
How to use checksums
bf8a2f109f08ef63276f81384c44ca489c737cb2da4365d2f0b2989d2222530c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Security for flow-analysis 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page