Python UI framework for interactive web dashboards and self-contained HTML reports, powered by bitwrench.js
Project description
webwrench
A Python library for building interactive web dashboards and self-contained HTML reports. It uses bitwrench.js for rendering and the bwserve protocol (SSE down, POST back) for live updates. No JavaScript required on your end, no build tools, no runtime dependencies.
- Script mode for quick dashboards, app mode with decorator-based routing for multi-page apps
- Static HTML export -- same API produces a single self-contained file you can open offline or email
- Chart.js included -- bar, line, pie, scatter, radar, and more without extra installs
- Callback-driven updates -- define the UI once, update individual elements via
on_change/on_click - Zero runtime dependencies --
pip install webwrenchpulls nothing else in
Installation
pip install webwrench
Requires Python 3.10+.
Quick Start
Script Mode
import webwrench as ww
ww.title("Hello World")
ww.text("My first webwrench app")
ww.serve()
Interactive Dashboard
import webwrench as ww
data = [12, 19, 3, 5, 2, 3]
ww.title("Sales Dashboard")
chart = ww.chart(data, type='bar', labels=['Jan','Feb','Mar','Apr','May','Jun'])
slider = ww.slider("Multiplier", min=1, max=10, value=1)
@slider.on_change
def update(value):
chart.update([d * value for d in data])
ww.serve()
Static HTML Report
import webwrench as ww
ww.title("Quarterly Report")
ww.chart([45, 67, 89, 34], type='line', labels=['Q1','Q2','Q3','Q4'])
ww.text("Revenue grew 48% year-over-year.")
ww.export('quarterly-report.html')
The exported file is self-contained -- open it in any browser, no server needed. Charts stay interactive (tooltips, hover, legend toggling).
Multi-Page App
import webwrench as ww
app = ww.App()
@app.page('/')
def home(ctx):
ctx.title("Dashboard")
ctx.chart([10, 20, 30], type='bar', labels=['A', 'B', 'C'])
@app.page('/settings')
def settings(ctx):
ctx.title("Settings")
theme = ctx.select("Theme", ['light', 'dark', 'ocean'])
@theme.on_change
def switch(value):
ctx.set_theme(value)
app.serve(port=6502)
API Reference
Display Elements
ww.title(text) # <h1>
ww.heading(text, level=2) # <h2>..<h6>
ww.text(text) # <p>
ww.markdown(md_string) # Rendered markdown
ww.code(code_string, lang='python') # Syntax-highlighted code block
ww.html(raw_html, raw=False) # HTML content (escaped by default)
ww.image(src, alt='', width=None) # Image
ww.divider() # <hr>
ww.table(data, sortable=False, searchable=False, paginate=None)
ww.metric(label, value, delta=None, delta_color=None)
ww.json(data, collapsed=1) # Collapsible JSON viewer
ww.progress(value=0, max_val=100) # Progress bar
ww.toast(message, type='info', duration=3000)
Input Widgets
All widgets return a handle with .value and .on_change(callback).
ww.button(label, on_click=None)
ww.input(label, placeholder='', value='')
ww.textarea(label, rows=4)
ww.slider(label, min=0, max=100, value=50, step=1)
ww.select(label, options=['a','b','c'], value='a')
ww.checkbox(label, value=False)
ww.radio(label, options=['x','y','z'])
ww.file_upload(label, accept='.csv,.json')
ww.date_picker(label)
ww.color_picker(label, value='#3366cc')
ww.number(label, min=0, max=100, step=1, value=0)
Charts
# Simple chart
ww.chart([12, 19, 3], type='bar', labels=['A','B','C'])
# Multi-dataset
ww.chart(datasets=[
{'label': 'Sales', 'data': [12, 19, 3], 'color': '#3366cc'},
{'label': 'Returns', 'data': [2, 3, 1], 'color': '#cc3333'}
], type='line', labels=['Jan','Feb','Mar'])
# From pandas DataFrame
ww.plot(df, x='date', y='revenue', type='line')
# Supported types: bar, line, pie, doughnut, radar, polarArea, scatter, bubble
Layout
with ww.columns(3) as cols:
with cols[0]: ww.text("Left")
with cols[1]: ww.chart(data1)
with cols[2]: ww.chart(data2)
with ww.tabs(['Overview', 'Details']) as t:
with t[0]: ww.title("Overview")
with t[1]: ww.table(detail_data)
with ww.accordion("Advanced Options", open=False):
ww.slider("Threshold", min=0, max=100, value=50)
with ww.card(title="Revenue"):
ww.chart(revenue_data, type='line')
with ww.sidebar():
ww.nav([{'text': 'Home', 'href': '/'}])
Theming
ww.theme('dark') # Built-in: light, dark, ocean, forest
ww.theme(primary='#006666') # Custom palette
ww.toggle_theme() # Toggle light/dark
ww.css({'.my-card': {'border-radius': '12px'}}) # Custom CSS
Export
ww.export('report.html') # Self-contained HTML file
ww.export('report.html', minify=True) # Minified output
ww.screenshot('dashboard.png') # Screenshot via html2canvas
ww.download('data.csv', content=csv_string) # Trigger browser download
How It Works
Python (webwrench) Browser
+------------------------+ +-------------------------+
| Your Python script | | bitwrench.js (bundled) |
| | | | +-- Chart.js |
| v | SSE --> | +-- bwclient.js |
| webwrench server | -------> | |
| (asyncio, built-in) | <------- | |
| | POST <-- | |
+------------------------+ +-------------------------+
- Frontend: bitwrench.js handles DOM operations. webwrench generates TACO (Tag, Attributes, Content, Options) dicts that bitwrench renders.
- Backend: Pure Python asyncio HTTP server. No Flask, no FastAPI, no external dependencies.
- Protocol: bwserve -- SSE for server-to-client updates, POST for client-to-server actions.
- Updates: Only the changed element is patched, not the whole page.
Development
git clone https://github.com/deftio/webwrench.git
cd webwrench
pip install -e ".[dev]"
# Run tests
pytest
# Run tests with coverage (100% required)
pytest --cov=webwrench --cov-report=term-missing --cov-fail-under=100
# Lint + security scan
ruff check webwrench/
bandit -r webwrench/ -c pyproject.toml
Releasing
Direct from main (quick):
./scripts/release.sh 0.2.0
This bumps the version, runs lint + tests + build, tags, and pushes.
Two-phase (for bigger releases):
# Phase 1: bump version + create release branch
./scripts/start-release.sh 0.2.0
# ... develop, commit, iterate ...
# Phase 2: validate, test, build, squash-merge to main, tag, push
./scripts/release.sh
After either flow, create a GitHub Release to publish to PyPI:
gh release create v0.2.0 --title "webwrench v0.2.0" --generate-notes
License
BSD-2-Clause. See LICENSE.txt.
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 webwrench-0.1.2.tar.gz.
File metadata
- Download URL: webwrench-0.1.2.tar.gz
- Upload date:
- Size: 251.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27782af7ce79b4df0dc12064df47d988878bf89db3cea1fc8a82b64e3fc6d1cd
|
|
| MD5 |
dbc9ca30b197caa3b1d39b42086d396a
|
|
| BLAKE2b-256 |
bdd73ab7f9774cd9bc6efead1bd6d5455eef5624a2f1c7730ab5ed249ac48c0a
|
Provenance
The following attestation bundles were made for webwrench-0.1.2.tar.gz:
Publisher:
publish.yml on deftio/webwrench
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
webwrench-0.1.2.tar.gz -
Subject digest:
27782af7ce79b4df0dc12064df47d988878bf89db3cea1fc8a82b64e3fc6d1cd - Sigstore transparency entry: 1192033326
- Sigstore integration time:
-
Permalink:
deftio/webwrench@a163a2c55fa39f2f962515950ed4d17bac13c17a -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/deftio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a163a2c55fa39f2f962515950ed4d17bac13c17a -
Trigger Event:
push
-
Statement type:
File details
Details for the file webwrench-0.1.2-py3-none-any.whl.
File metadata
- Download URL: webwrench-0.1.2-py3-none-any.whl
- Upload date:
- Size: 82.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c652928ecdd8e3e9d6fc52be6fdd8797a8c35c504e1bb17e32f4209596553823
|
|
| MD5 |
a14b669b5013c464f6f2c28cf66bde0a
|
|
| BLAKE2b-256 |
4a0b299464667acd18b61ccfeff8ef88a2c13b1f8d5e044ae75058ebb07d9a93
|
Provenance
The following attestation bundles were made for webwrench-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on deftio/webwrench
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
webwrench-0.1.2-py3-none-any.whl -
Subject digest:
c652928ecdd8e3e9d6fc52be6fdd8797a8c35c504e1bb17e32f4209596553823 - Sigstore transparency entry: 1192033333
- Sigstore integration time:
-
Permalink:
deftio/webwrench@a163a2c55fa39f2f962515950ed4d17bac13c17a -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/deftio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a163a2c55fa39f2f962515950ed4d17bac13c17a -
Trigger Event:
push
-
Statement type: