https://github.com/jhd3197/cacaodocs
CacaoDocs is a lightweight Python package that effortlessly extracts API documentation directly from your code's docstrings
https://github.com/jhd3197/cacaodocs
api api-documentation docs docstring docstrings documentation documentation-generator documentation-tool er-diagram flexsearch python
Last synced: 3 months ago
JSON representation
CacaoDocs is a lightweight Python package that effortlessly extracts API documentation directly from your code's docstrings
- Host: GitHub
- URL: https://github.com/jhd3197/cacaodocs
- Owner: jhd3197
- License: mit
- Created: 2024-12-30T11:11:44.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2026-03-05T07:12:17.000Z (3 months ago)
- Last Synced: 2026-03-05T11:42:38.556Z (3 months ago)
- Topics: api, api-documentation, docs, docstring, docstrings, documentation, documentation-generator, documentation-tool, er-diagram, flexsearch, python
- Language: Python
- Homepage: https://jhd3197.github.io/CacaoDocs/
- Size: 11.5 MB
- Stars: 9
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# CacaoDocs
A documentation generator for Python projects, powered by [Cacao](https://github.com/cacao-research/Cacao).
CacaoDocs scans your Python source code, parses docstrings, and generates an interactive documentation app with WebSocket-driven reactivity. No static HTML — your docs are a live app.
## Links
- **PyPI:** [cacaodocs](https://pypi.org/project/cacaodocs/)
- **GitHub:** [CacaoDocs](https://github.com/jhd3197/CacaoDocs)
## Installation
```bash
pip install cacaodocs
```
## Quick Start
```bash
# Initialize config (optional)
cacaodocs init
# Build docs from your source directory
cacaodocs build ./src -o ./docs
# Serve the generated docs
cacaodocs serve ./docs
```
## How Docstrings Work
CacaoDocs uses **Google-style docstrings** with one addition: a `Type:` directive that tells the parser what kind of thing you're documenting. If you don't specify a type, it defaults to `function`.
Here's the idea — you write normal docstrings, and CacaoDocs figures out what sections to parse based on the type:
| Type | What it's for | How it's detected |
|------|--------------|-------------------|
| `function` | Regular functions and methods | Default — no directive needed |
| `api` | REST API endpoints | Auto-detected from decorators like `@app.get`, `@router.post`, `@app.route` |
| `class` | Python classes | Detected from class definitions |
| `page` | Markdown documentation pages | `.md` files in your source directory |
| `config` | App settings and env vars | Set with `Type: config` |
| `event` | Webhooks, signals, pub/sub | Set with `Type: event` |
| Custom | Anything you define | Set with `Type: your_type_name` |
### Function (default)
The most common type. Just write standard Google-style docstrings — no `Type:` directive needed:
```python
def hash_password(password: str, salt: str = None) -> str:
"""Hash a password using bcrypt.
Args:
password (str): The plaintext password.
salt (str): Optional salt. Generated if not provided.
Returns:
str: The hashed password string.
Raises:
ValueError: If password is empty or too short.
Examples:
>>> hash_password("mysecretpass")
'$2b$12$...'
"""
```
**Available sections:** `Args`, `Returns`, `Raises`, `Examples`, `Notes`
### API Endpoints
API endpoints are **auto-detected** from Flask, FastAPI, and Django REST decorators — you don't need to add `Type: api`. CacaoDocs sees `@app.get(...)` and knows it's an API endpoint, extracting the HTTP method and route path for you.
```python
@app.get("/users/{user_id}")
def get_user(user_id: int):
"""Get a user by ID.
Path Params:
user_id (int): The user's unique identifier.
Query Params:
include (str): Comma-separated fields to include.
Response (200):
id (int): The user ID.
name (str): Full name.
email (str): Email address.
Response (404):
detail (str): User not found.
"""
```
**Auto-detected decorators:**
- **FastAPI**: `@app.get`, `@app.post`, `@router.put`, `@router.delete`, `@router.patch`
- **Flask**: `@app.route`, `@blueprint.route`
- **Django REST**: `@api_view`
**Available sections:** `Path Params`, `Query Params`, `Request Body`, `Headers`, `Response (NNN)`
### Config
For documenting application settings and environment variables. Use `Type: config` to enable config-specific parsing:
```python
def load_settings():
"""Application settings.
Type: config
Fields:
DEBUG (bool, default=False): Enable debug mode.
SECRET_KEY (str, required, env=APP_SECRET): Secret key for signing.
DATABASE_URL (str, required, env=DATABASE_URL): PostgreSQL connection string.
PORT (int, default=8000): Server port.
"""
```
Each field supports modifiers inside the parentheses:
- **type** — `str`, `int`, `bool`, etc.
- **`default=value`** — Default value
- **`required`** — Marks the field as required
- **`env=VAR_NAME`** — Maps to an environment variable
### Event
For documenting webhooks, signals, and event-driven patterns. Use `Type: event`:
```python
def on_user_signup(data: dict):
"""User signup event.
Type: event
Trigger: When a new user completes registration.
Payload:
user_id (int): The new user ID.
email (str): The user's email.
plan (str): Selected subscription plan.
"""
```
**Available sections:** `Trigger` (inline directive), `Payload`
### Custom Types
You can define your own doc types in `cacao.yaml`. Each custom type gets its own sections and display settings:
```yaml
doc_types:
cli_command:
label: "CLI Command"
icon: "terminal"
sections:
- name: "Usage"
format: code
- name: "Options"
format: args
- name: "Flags"
format: args
database_model:
label: "Model"
icon: "database"
sections:
- name: "Fields"
format: args
- name: "Indexes"
- name: "Relations"
```
Then use them with the `Type:` directive:
```python
def deploy():
"""Deploy the application.
Type: cli_command
Usage:
deploy --env production --tag v1.2.3
Options:
env (str): Target environment.
tag (str): Version tag to deploy.
"""
```
## Configuration
Create a `cacao.yaml` in your project root:
```yaml
title: "My Project"
description: "API Documentation"
version: "1.0.0"
theme: "dark"
github_url: "https://github.com/username/repo"
exclude_patterns:
- "__pycache__"
- ".venv"
- "node_modules"
```
## Deploy to GitHub Pages
CacaoDocs can export a static version of your docs and deploy them to GitHub Pages. Add this workflow to `.github/workflows/docs.yml`:
```yaml
name: Deploy Docs to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: true
jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: |
pip install cacaodocs
pip install "cacao>=2.1.0"
- name: Build docs
run: cacaodocs build ./src -o ./_build
- name: Export static site
run: cacaodocs export ./_build -o ./dist --base-path /${{ github.event.repository.name }}
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: dist
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v4
```
**Setup steps:**
1. Go to your repo's **Settings > Pages**
2. Set **Source** to **GitHub Actions**
3. Push the workflow file to `main` — it will build and deploy automatically
> **Note:** Change `./src` in the build step to wherever your Python source code lives. The `--base-path` flag is needed so links work correctly under `https://username.github.io/repo-name/`.
## The `@doc()` Decorator
Instead of (or in addition to) docstrings, you can use the `@doc()` decorator to define documentation as structured data. This avoids fragile docstring parsing entirely — CacaoDocs reads the keyword arguments directly from the AST.
```python
from cacaodocs import doc
@doc(
description="Fetch a user by ID.",
args={"user_id": {"type": "int", "description": "The user's unique identifier"}},
returns={"type": "User", "description": "The matching user"},
raises={"NotFoundError": "If user doesn't exist"},
deprecated="2.0",
category="Users",
version="1.0",
)
def get_user(user_id: int) -> User:
...
```
The decorator is a **runtime no-op** — it just returns your function unchanged. Zero overhead. CacaoDocs reads the keyword arguments at scan time via AST, so there's nothing to parse or misparse.
### Decorator vs Docstring
You can use either approach, or both. When both exist, **decorator values win**.
```python
# Decorator only — no docstring needed
@doc(description="Add two numbers.", args={"a": "First", "b": "Second"}, returns="The sum")
def add(a, b):
return a + b
# Supplement mode — docstring provides the basics, decorator adds metadata
@doc(category="Utils", version="3.0", deprecated=True)
def helper():
"""Reticulate the splines.
Args:
count (int): Number of splines.
"""
...
```
### Supported kwargs
**General:**
| Kwarg | Type | Description |
|-------|------|-------------|
| `description` | `str` | Summary text |
| `doc_type` | `str` | Override type: `"api"`, `"config"`, `"event"`, etc. |
| `deprecated` | `bool \| str` | `True` or a since-version string like `"2.0"` |
| `category` | `str` | Sidebar group name |
| `version` | `str` | Version when added |
| `hidden` | `bool` | Exclude from generated docs |
**Function sections:**
| Kwarg | Simple syntax | Full syntax |
|-------|--------------|-------------|
| `args` | `{"name": "desc"}` | `{"name": {"type": "str", "description": "...", "default": "x"}}` |
| `returns` | `"description"` | `{"type": "User", "description": "..."}` |
| `raises` | `{"ValueError": "when bad"}` | — |
| `examples` | `["example()"]` | — |
| `notes` | `["Note text"]` | — |
| `attributes` | Same as `args` | Same as `args` |
**API sections:**
| Kwarg | Type | Description |
|-------|------|-------------|
| `method` | `str` | HTTP method (`"GET"`, `"POST"`, etc.) |
| `path` | `str` | Route path (`"/users/{id}"`) |
| `path_params` | `dict` | Same format as `args` |
| `query_params` | `dict` | Same format as `args` |
| `request_body` | `dict` | Same format as `args` |
| `responses` | `dict` | `{200: "OK"}` or `{200: {"description": "...", "fields": {...}}}` |
| `headers` | `dict` | `{"Auth": "desc"}` or `{"Auth": {"description": "...", "required": True}}` |
**Event sections:**
| Kwarg | Type | Description |
|-------|------|-------------|
| `trigger` | `str` | Event trigger name |
| `payload` | `dict` | `{"field": {"type": "int", "description": "..."}}` |
**Config sections:**
| Kwarg | Type | Description |
|-------|------|-------------|
| `config_fields` | `dict` | `{"KEY": {"type": "bool", "default": "false", "env": "APP_KEY"}}` |
## Features
- **`@doc()` Decorator** — Structured metadata as an alternative to docstrings
- **Doc Types** — Function, API, Class, Page, Config, Event, and Custom types
- **Auto-Detection** — Flask/FastAPI/Django endpoints detected from decorators
- **Deprecation Tracking** — From decorators, docstrings, or `@doc(deprecated="2.0")`
- **Call Map** — Visualize function call relationships across your codebase
- **Dashboard** — Coverage scores, complexity hotspots, TODOs, dead code detection
- **Interactive App** — Generated docs are a live Cacao app, not static HTML
- **Google-style Docstrings** — Extended with API, config, and event sections
- **Custom Types** — Define your own doc types via `cacao.yaml`
- **Static Export** — Deploy to GitHub Pages or any static host
## Contributing
Contributions are welcome — issues, feature requests, and pull requests.
## License
MIT