Generate business flow diagrams from Python code decorators.
Unlike tools that trace technical execution paths, FlowDoc captures business logic flow -- the high-level process steps that describe what your application does from a business perspective. Annotate your code with lightweight decorators, and FlowDoc uses AST analysis to infer the flow graph and render it as a diagram.
- Inference over declaration -- annotate steps, and FlowDoc discovers connections by analyzing your code
- Pure AST analysis -- no code execution, safe to run on untrusted code
- Cross-module discovery -- analyze flows spanning multiple files with automatic file discovery
- Multiple output formats -- Mermaid markdown (default), PNG, SVG, PDF, DOT (Graphviz formats require optional
graphvizextra) - Docstring tooltips -- include
@stepdocstrings as tooltips in SVG/DOT/Mermaid output - Flexible patterns -- class-based flows, standalone functions, async/await, and web frameworks (FastAPI, Flask)
- Built-in validation -- detect dead steps, missing entry points, and other flow issues
- CLI included -- generate diagrams and validate flows from the command line
pip install flowdocFlowDoc requires Python 3.10+. The base install supports Mermaid and DOT output with no extra dependencies.
For PNG, SVG, and PDF output, install the Graphviz extra and the Graphviz system package:
pip install flowdoc[graphviz]Decorate your business logic with @flow and @step:
from flowdoc import flow, step
@flow(name="Order Processing", description="Handle customer orders")
class OrderProcessor:
@step(name="Receive Order")
def receive_order(self, order_data):
return self.validate_payment(order_data)
@step(name="Validate Payment")
def validate_payment(self, order):
if order.get("payment_valid"):
return self.fulfill_order(order)
else:
return self.send_failure_email(order)
@step(name="Fulfill Order")
def fulfill_order(self, order):
return self.send_confirmation(order)
@step(name="Send Confirmation")
def send_confirmation(self, order):
return {"status": "confirmed", "order": order}
@step(name="Send Failure Email")
def send_failure_email(self, order):
return {"status": "failed", "order": order}Generate the diagram:
flowdoc generate order_processor.pyFlowDoc analyzes the code, detects that validate_payment branches into two paths, and produces a flowchart with decision diamonds, regular steps, and terminal nodes.
# Generate a Mermaid diagram (default)
flowdoc generate mymodule.py
# Choose output format
flowdoc generate mymodule.py --format svg
flowdoc generate mymodule.py --format png
flowdoc generate mymodule.py --format dot
# Generate from a directory (cross-module discovery)
flowdoc generate src/
# Include docstrings as tooltips (SVG, DOT, Mermaid only)
flowdoc generate mymodule.py --format svg --docstrings
# Exclude directories during discovery
flowdoc generate src/ --exclude migrations --exclude scripts
# Validate a flow for issues
flowdoc validate mymodule.pyfrom flowdoc import FlowParser, create_generator, FlowValidator
# Parse a single file
parser = FlowParser()
flows = parser.parse_file("mymodule.py")
# Or parse a directory (cross-module discovery)
flows = parser.parse_directory("src/")
# Generate a diagram
generator = create_generator("mermaid")
for flow_data in flows:
generator.generate(flow_data, "output")
# Generate with docstring tooltips
generator = create_generator("svg", include_docstrings=True)
for flow_data in flows:
generator.generate(flow_data, "output")
# Validate
validator = FlowValidator()
for flow_data in flows:
messages = validator.validate(flow_data)
for msg in messages:
print(f"[{msg.level}] {msg.message}")Use @flow on a class and @step on its methods. FlowDoc detects self.method() calls to build the graph.
Use @step on standalone functions. FlowDoc detects direct function calls between decorated functions.
from flowdoc import step
@step(name="Process Order")
def process_order(order_data):
validated = validate_order(order_data)
return charge_payment(validated)
@step(name="Validate Order")
def validate_order(order_data):
return order_data
@step(name="Charge Payment")
def charge_payment(order):
return {"status": "charged"}from flowdoc import step
@step(name="Create Order")
async def create_order(order):
validated = await validate_order(order)
return await save_order(validated)Works alongside FastAPI, Flask, and other decorator-based frameworks:
from fastapi import FastAPI
from flowdoc import step
app = FastAPI()
@app.post("/orders")
@step(name="Create Order Endpoint")
async def create_order(order: OrderData):
validated = await validate_order(order)
return await save_order(validated)See examples/fastapi/app.py for a complete CRUD API example with @step decorators on endpoints.
For FastAPI/Flask app-factory patterns, decorate the factory function itself with @flow. FlowDoc treats it as a flow boundary and discovers nested @step inner functions:
from fastapi import FastAPI
from flowdoc import flow, step
@flow(name="Order API")
def create_app() -> FastAPI:
app = FastAPI()
@app.post("/orders")
@step(name="Create Order Endpoint")
async def create_order(payload: dict):
return await validate_order(payload)
@step(name="Validate Order")
async def validate_order(payload: dict):
return payload
return appSee examples/fastapi_factory.py for a runnable example.
try/except/finally blocks are detected and labeled on edges, so business error paths (payment declines, retries, failure notifications) appear in the diagram:
@step(name="Process Payment")
def process_payment(self, order):
try:
return self.charge_card(order)
except PaymentError:
return self.send_failure_email(order)
except NetworkError:
return self.retry_later(order)
finally:
self.log_attempt(order)Edges are labeled try, except PaymentError, except NetworkError, and finally. See examples/payment_retry.py.
FlowDoc uses Python's ast module to statically analyze decorated functions. For each @step, it walks the function body looking for:
- Method calls (
self.other_method()) and function calls (other_function()) to other@step-decorated code - Conditional branches (
if/else) to identify decision points - Exception handling (
try/except/finally) -- except handlers label edges with the exception type (e.g.except PaymentError), enabling business error-handling flows - Call count to determine node shapes: multiple outgoing calls produce a diamond (decision), zero calls produce an ellipse (terminal), and single calls produce a box (regular step)
No code is ever executed during analysis.
These are intentional design constraints -- FlowDoc focuses on explicit, readable business flows:
- Dynamic decorator arguments: Use literal strings (
@step(name="Process")) rather than variables - Dynamic dispatch:
getattr(self, name)()is not traced -- use explicit method calls - External object calls:
processor.handle()is not followed unless that object's methods are also decorated - Indirect references: Passing functions as arguments is not traced -- use explicit
if/else
PNG, SVG, and PDF output requires both the Python graphviz package and the Graphviz system package.
Install the Python extra:
pip install flowdoc[graphviz]Install the system package:
- macOS:
brew install graphviz - Ubuntu/Debian:
sudo apt-get install graphviz - Windows: Download from graphviz.org
Mermaid and DOT formats do not require Graphviz.
Ensure your source file contains functions or methods decorated with @step. If using class-based flows, the class must also have the @flow decorator. FlowDoc only analyzes @step and @flow decorators -- it does not detect undecorated functions.
FlowDoc only supports literal string arguments in decorators. This works:
@step(name="Process Order") # literal stringThis does not:
step_name = "Process Order"
@step(name=step_name) # variable -- not supportedUse literal strings in all decorator arguments for FlowDoc to detect them.
If you see ModuleNotFoundError: No module named 'flowdoc' after installation:
# Ensure you're using the correct Python environment
python -m flowdoc --version
# Or reinstall in your active environment
pip install --force-reinstall flowdoc# Install uv (if needed)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies
uv sync
# Install in development mode
uv pip install -e .# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=flowdoc --cov-report=term-missing
# Run a specific test
uv run pytest tests/test_parser.py::test_class_flow_parsing# Format
uv run ruff format .
# Lint (with auto-fix)
uv run ruff check . --fix
# Type check
uv run ty check flowdoc/flowdoc/
βββ flowdoc/
β βββ __init__.py # Public API exports
β βββ decorators.py # @flow and @step decorators
β βββ discovery.py # Cross-module file discovery
β βββ parser.py # AST analysis and flow extraction
β βββ generator.py # Graphviz and Mermaid diagram generators
β βββ validator.py # Flow validation logic
β βββ cli.py # CLI (Click-based)
βββ tests/ # pytest test suite
βββ examples/ # Example flows
βββ pyproject.toml
βββ LICENSE # MIT
Contributions are welcome. To get started:
- Fork the repository and create a feature branch
- Install development dependencies with
uv sync - Make your changes, adding tests for new functionality
- Ensure all checks pass:
uv run pytest,uv run ruff check .,uv run ty check flowdoc/ - Submit a pull request
Please keep decorators minimal, avoid executing user code, and treat validation as advisory (warnings, not errors).
MIT -- see LICENSE for details.