Skip to main content

High-performance reactive web framework for Python. Build dashboards, internal tools, and data apps with a Streamlit-like API but 10x faster.

Project description

Cacao

A clean-slate rewrite of Cacao with a clear separation of concerns:

  • Python Server: Manages state (Signals) and handles events
  • React Client: Renders the UI and dispatches events
  • WebSocket: JSON state sync between server and client

Quick Start

1. Install Python Dependencies

pip install -r cacao/requirements.txt

2. Create a Simple Server

# app.py
from cacao import App, Signal

app = App()
count = Signal(0, name="count")

@app.on("increment")
async def increment(session, event):
    count.set(session, count.get(session) + 1)

if __name__ == "__main__":
    app.run(port=8000)

3. Set Up the React Client

cd cacao/client
npm install
npm run dev

4. Use Signals in React

import { CacaoProvider, useSignal, useEvent } from './cacao'

function Counter() {
  const count = useSignal('count', 0)
  const increment = useEvent('increment')

  return (
    <button onClick={() => increment()}>
      Count: {count}
    </button>
  )
}

export default function App() {
  return (
    <CacaoProvider url="ws://localhost:8000/ws">
      <Counter />
    </CacaoProvider>
  )
}

Core Concepts

Signals (Server-Side State)

Signals are reactive state containers with session scoping:

from cacao import Signal

# Create a signal with a default value
name = Signal("", name="name")

# Get value for a session
current_name = name.get(session)

# Set value (automatically syncs to client)
name.set(session, "John")

# Update using a function
name.update(session, lambda n: n.upper())

Events (Client-to-Server)

Events are the mechanism for client actions:

@app.on("submit")
async def handle_submit(session, event):
    # event is a dict with the data from the client
    print(event.get("value"))

Auto-Binding (Form Helpers)

Bind events directly to signals for form inputs:

name = Signal("", name="name")
app.bind("name:input", name)  # Auto-updates signal on input

# Client just sends: { type: "event", name: "name:input", data: { value: "..." } }

React Hooks

// Subscribe to a signal
const count = useSignal<number>('count', 0)

// Create an event dispatcher
const submit = useEvent('submit')
submit({ value: 'hello' })

// Check connection status
const status = useConnectionStatus()  // 'connecting' | 'connected' | 'disconnected' | 'reconnecting'

// Get session ID
const sessionId = useSessionId()

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         PYTHON SERVER                            │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌───────────────┐   │
│  │ Signals  │  │ Sessions │  │  Events  │  │ WebSocket/HTTP│   │
│  │ (state)  │──│ (per-    │──│ (typed   │──│    Server     │   │
│  │          │  │  client) │  │  async)  │  │               │   │
│  └──────────┘  └──────────┘  └──────────┘  └───────┬───────┘   │
└────────────────────────────────────────────────────┼───────────┘
                                                     │ JSON
                                                     │ WebSocket
┌────────────────────────────────────────────────────┼───────────┐
│                      REACT CLIENT                   │           │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────┴──────┐    │
│  │  Hooks   │  │Components│  │  Event   │  │   State     │    │
│  │useCacao()│──│ (your UI)│──│Dispatcher│──│   Store     │    │
│  └──────────┘  └──────────┘  └──────────┘  └─────────────┘    │
└─────────────────────────────────────────────────────────────────┘

WebSocket Protocol

Server → Client

// Initial state
{ "type": "init", "state": { "count": 0 }, "sessionId": "abc123" }

// State update
{ "type": "update", "changes": { "count": 1 } }

Client → Server

// Event dispatch
{ "type": "event", "name": "increment", "data": {} }

File Structure

cacao/
├── server/                 # Python package
│   ├── __init__.py
│   ├── app.py              # App class, decorators
│   ├── signal.py           # Signal, Computed
│   ├── session.py          # Session management
│   ├── events.py           # Event handling
│   └── server.py           # WebSocket server
│
├── client/                 # React + Vite
│   ├── package.json
│   ├── src/
│   │   ├── cacao/          # Cacao client library
│   │   │   ├── index.ts
│   │   │   ├── hooks.ts
│   │   │   ├── store.ts
│   │   │   ├── websocket.ts
│   │   │   └── types.ts
│   │   └── App.tsx
│   └── ...
│
├── examples/
│   └── counter/            # Counter example
│
└── requirements.txt

Examples

See cacao/examples/counter/ for a working example.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cacao-2.0.0.tar.gz (145.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cacao-2.0.0-py3-none-any.whl (173.4 kB view details)

Uploaded Python 3

File details

Details for the file cacao-2.0.0.tar.gz.

File metadata

  • Download URL: cacao-2.0.0.tar.gz
  • Upload date:
  • Size: 145.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for cacao-2.0.0.tar.gz
Algorithm Hash digest
SHA256 26e2bae69328049132b7f5425926c20ffa75e232c214acc283100465fdad183e
MD5 a429617fef0dd3903d0999f14511129c
BLAKE2b-256 a1a9d8c82f8db7a86a0a1ec6491b54ffd54b2a9c9b0e8506d0c2b90291af16be

See more details on using hashes here.

File details

Details for the file cacao-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: cacao-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 173.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for cacao-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 146ebfbb5ffb2d2f78aba33fdd575010072d8980b8254f7102105caa7c8b1d71
MD5 71d5924d8ae68447c8a40f94d9f615f6
BLAKE2b-256 e11fabe69e6cd5fb991ca7c095d03854f99011b0a58238b7b1097038d0008bf8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page