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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26e2bae69328049132b7f5425926c20ffa75e232c214acc283100465fdad183e
|
|
| MD5 |
a429617fef0dd3903d0999f14511129c
|
|
| BLAKE2b-256 |
a1a9d8c82f8db7a86a0a1ec6491b54ffd54b2a9c9b0e8506d0c2b90291af16be
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
146ebfbb5ffb2d2f78aba33fdd575010072d8980b8254f7102105caa7c8b1d71
|
|
| MD5 |
71d5924d8ae68447c8a40f94d9f615f6
|
|
| BLAKE2b-256 |
e11fabe69e6cd5fb991ca7c095d03854f99011b0a58238b7b1097038d0008bf8
|