Thanks to visit codestin.com
Credit goes to github.com

Skip to content

lbliii/chirp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

659 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

⌁⌁ Chirp

PyPI version Python 3.14+ License: MIT Status: Alpha

A full-stack Python hypermedia framework with a built-in contract compiler.

Routes return intent — Page, Fragment, EventStream, Suspense, and friends — and Chirp handles content negotiation, layout composition, and htmx awareness. Install as bengal-chirp, import as chirp. Requires Python 3.14+.

Chirp ships routing, templates, forms, validation, sessions, auth, streaming HTML, SSE, static files, security middleware, testing tools, and hypermedia contract checks in one framework. At startup it compiles routes, typed return declarations, template blocks, and registries into one immutable internal application model used by chirp check, runtime transition traces, and testing tools. JSON routes and explicit Response objects are supported when you need them. Database access uses Shapes and an optional in-tree PostgreSQL driver. Background jobs, admin UIs, and email delivery integrate at the seams — see Non-goals.

Status: alpha (0.9.x). See Public API for stable vs provisional exports. Public positioning and performance language is governed by the machine-checked claims ledger.

📚 Documentation: lbliii.github.io/chirp


Quick start

pip install 'bengal-chirp[ui]'   # [ui] optional but recommended for new projects
chirp new myapp && cd myapp
python app.py                      # http://127.0.0.1:8000
chirp check myapp:app              # validate hypermedia wiring

The scaffold includes routes, templates, and (with [ui]) ChirpUI layouts. No npm, no build step.

Minimal example (no scaffold)
from chirp import App

app = App()

@app.route("/")
def index():
    return "Hello, World!"

app.run()

For the smallest complete htmx loop (Page, Fragment, forms, tests), follow First Fragment App.


The core idea

One template, many access patterns. The return type expresses intent; Chirp negotiates the response:

from chirp import App, Page, Request

app = App()

@app.route("/search")
async def search(request: Request):
    results = await db.search(request.query.get("q", ""))
    return Page("search.html", "results", results=results)
    # Browser navigation → full page. htmx request → just the "results" block.

No make_response(). No separate partials directory. The type is the intent.

Read Philosophy and Return values for the full model.

The same declarations form Chirp's contract compiler input. chirp check diagnoses broken route/template/target relationships before a browser reaches them, while DevTools and transition tests correlate runtime requests back to the compiled model. The primary output is still a live ASGI application; chirp freeze is an optional static projection for compatible routes.

See Hypermedia Application Compiler for the architecture and the tested Full-Application Journey for the database, mutation, validation, boosted-navigation, SSE, diagnostics, and optional-export proof.


Where to go next

I want to… Start here
Learn step by step Learning path · Get Started
Understand the architecture About · Core concepts
Build features Build Apps
Prove a complete database-backed app Full-Application Journey
Run runnable examples Examples index
Compare to other stacks When to use Chirp
See what's intentionally out of scope Non-goals
Look up exports and stability Public API · Reference · Glossary
Contracts, tests, deployment Quality & Operations

Learn Chirp (examples)

Follow the learning path on the docs site. Examples are tiered on purpose. Do them in order.

Tier Example You will learn
1 — Basics standalone/hello, standalone/contacts Routes, forms, Page / Fragment, validation
2 — App shell chirpui/contacts_shell ChirpUI shell, _actions.py, _context.py, boosted nav
3 — Capstone chirpui/lucky_cat Signals, Suspense, SSE, OOB, secure stack
Capstone demo — Lucky Cat (tier 3, not the on-ramp)

Live: luckycat-production.up.railway.app · Source: examples/chirpui/lucky_cat/

A simulated trading-floor UI built on server-owned signals, SSE, Suspense, and OOB swaps — no client framework. Complete tiers 1–2 first.

Most day-to-day apps use a small set: App, @app.route, Template, Page, forms, ValidationError, and chirp check. Streaming, signals, and filesystem routing are the next layer — the tiered examples introduce them in order.


Installation

# pip
pip install bengal-chirp

# uv
uv add bengal-chirp

The packaged chirp command uses the direct milo-cli 0.4.x dependency for typed, lazy command registration. Existing Chirp argv and exit behavior remain covered by a black-box compatibility suite. The read-only check, diff, and routes inspections share structured results across CLI, programmatic calls, MCP, and llms.txt; server, scaffold, freeze, migration, and other write-capable commands remain human-only.

Optional extras
Extra Adds
[ui] chirp-ui components and themes (chirp new emits ChirpUI layouts)
[forms] Multipart form parsing
[sessions] Signed cookie sessions
[auth] Argon2 password hashing
[passkeys] WebAuthn / passkeys
[ai] LLM streaming (httpx)
[data-pg] PostgreSQL via in-tree driver (no extra deps)
[testing] httpx test client transport
[redis] Redis-backed sessions and rate limiting
[markdown] Patitas + Rosettes markdown rendering
[config] python-dotenv for .env loading
[all] / [full] Common optional features bundled
pip install 'bengal-chirp[ui]'
# or: uv add 'bengal-chirp[ui]'

When chirp-ui is installed, chirp check verifies that chirpui-* classes resolve to backing styles.


Reference

CLI
Command Description
chirp new <name> Scaffold an auth-ready project
chirp new <name> --shell Scaffold with a persistent app shell (topbar + sidebar)
chirp new <name> --stream Simulated token streaming (TemplateStream + EventStream)
chirp new <name> --sse Scaffold with SSE boilerplate (EventStream, sse_scope)
chirp new <name> --ai Scaffold AI chat with tools, SSE activity feed, and secure stack
chirp run <app> Start the dev server from an import string
chirp dev <app> Dev server with Chirp DevTools
chirp check <app> Validate hypermedia contracts
chirp check <app> --warnings-as-errors Fail CI on contract warnings
chirp check <app> --coverage Show contract coverage counters
chirp check <app> --deploy Deploy preflight (implies --warnings-as-errors)
chirp routes <app> Print the registered route table
chirp --version Print chirp, kida, pounce, and Python versions
Return types — type-driven content negotiation
return "Hello"                                   # -> 200, text/html
return {"users": [...]}                          # -> 200, application/json
return Template("page.html", title="Home")        # -> 200, rendered via Kida
return Page("search.html", "results", items=x)   # -> Fragment or Template (auto)
return Fragment("page.html", "results", items=x) # -> 200, rendered block
return Stream("dashboard.html", **async_ctx)     # -> 200, streamed HTML
return Suspense("dashboard.html", stats=...)     # -> shell + OOB swaps
return EventStream(generator())                  # -> SSE stream
return hx_redirect("/dashboard")                 # -> Location + HX-Redirect
return Response(body=b"...", status=201)          # -> explicit control
return Redirect("/login")                        # -> 302

For htmx-driven form posts that should trigger full-page navigation, prefer hx_redirect() so both plain browser and htmx requests follow the redirect correctly.

Experimental HTTP QUERY — safe body-bearing searches

Chirp supports RFC 10008 QUERY on explicit ASGI routes for controlled early-adopter use. Choose it only when a read-only query is too large or structured for a practical URI; ordinary bookmarkable searches and native HTML forms should stay GET.

The route declares accepted request media types, while the handler keeps using Chirp's normal typed HTML returns and one-template/named-block render surface. Browser, Pounce, Uvicorn, and Nginx proof exists, but stable promotion and universal proxy/CDN support are not claimed. Keep a GET fallback and verify the exact deployment path.

See the HTTP QUERY adoption guide for request failures, CORS, redirects, conditional responses, explicit cache opt-in, compatibility evidence, and the remaining release gates.

Stream vs Suspense vs EventStream

Picking the wrong one is the most common return-type mistake:

Type Shell first? Transport Use for Not for
Stream No — flush blocks as they complete Single chunked HTTP response Slow first-byte pages with independent sections Post-load updates
Suspense Yes — shell renders, deferred blocks stream as OOB swaps Single chunked HTTP response Dashboards with multiple slow data sources, one round trip Post-load updates
EventStream N/A — pure event channel SSE (text/event-stream, long-lived) Notifications, tickers, chat tails after the page loads Initial page render

Rule of thumb: initial render that streams → Suspense (or Stream for SEO-heavy sections); updates after the page loads → EventStream for page-local regions, signal() for cross-page chrome. Multi-target mutations → OOB / FormAction.

See the realtime decision tree.

Fragments and htmx
{# templates/search.html #}
{% extends "base.html" %}

{% block content %}
  <input type="search" hx-get="/search" hx-target="#results" name="q">
  {% block results_list %}
    <div id="results">
      {% for item in results %}
        <div class="result">{{ item.title }}</div>
      {% end %}
    </div>
  {% endblock %}
{% endblock %}
@app.route("/search")
async def search(request: Request):
    results = await db.search(request.query.get("q", ""))
    if request.is_fragment:
        return Fragment("search.html", "results_list", results=results)
    return Template("search.html", results=results)
Forms and validation
from chirp import Page, ValidationError
from chirp.validation import validate, required, email, max_length

@app.route("/contacts", methods=["POST"])
async def create_contact(request: Request):
    form = await request.form()
    result = validate(form, {
        "name":  [required, max_length(200)],
        "email": [required, email],
    })
    if not result:
        return ValidationError("contacts.html", "form", errors=result.errors, form=form)
    contacts.append(Contact(**result.data))
    return Page("contacts.html", "list", contacts=contacts)

ValidationError returns 422 with the re-rendered form fragment for htmx; non-htmx requests get the full page back.

Server-Sent Events
@app.route("/notifications")
async def notifications(request: Request):
    async def stream():
        async for event in notification_bus.subscribe(request.user):
            yield Fragment("components/notification.html", event=event)
    return EventStream(stream())

Combined with htmx's SSE support, the server renders HTML and the browser swaps it in. The managed htmx 4 preview uses native hx-sse:connect: rendered Fragment updates are unnamed HTML frames, and Fragment(target="feed") becomes an unnamed <hx-partial hx-target="#feed"> update. Named SSEEvents remain DOM events. Htmx 2 keeps its existing sse-connect / sse-swap channels.

Middleware

No base class. No inheritance. A middleware is anything that matches the protocol:

async def timing(request: Request, next: Next) -> Response:
    start = time.monotonic()
    response = await next(request)
    elapsed = time.monotonic() - start
    return response.with_header("X-Time", f"{elapsed:.3f}")

app.add_middleware(timing)

Built-in middleware: CORS, StaticFiles, HTMLInject, Sessions, SecurityHeaders, CSRF, Auth, and more. See Request pipeline.

Contracts — static hypermedia validation
app.check()                        # report and exit non-zero on errors
app.check(warnings_as_errors=True) # strict mode

Every hx-get, hx-post, and action attribute in templates is checked against the route table. Every Fragment and SSE return type is checked against available template blocks.

chirp check myapp:app --warnings-as-errors

See Contracts.

DevTools
chirp dev myapp:app

Open the app in a browser and press Ctrl+Shift+D for Chirp DevTools — htmx activity, effective hx-* inheritance, render plans, EventStream traces, View Transitions, and Swap Doctor warnings.

window.ChirpHtmxDebug.help()
window.ChirpHtmxDebug.exportRecordsJson()
Features index
Topic Docs
HTMX patterns htmx Patterns
Routing & filesystem layout Pages & navigation
Templates & fragments HTML fragments
Forms & data Forms & validation
Streaming & SSE Streaming updates
Middleware Request pipeline
Contracts & debugging Quality
Testing Testing
Optional UI layer chirp-ui

Production

Chirp apps run on Pounce, a production-grade ASGI server with HTTP/2, graceful shutdown, Prometheus metrics, rate limiting, and multi-worker scaling.

chirp check myapp:app --warnings-as-errors   # hypermedia contracts
pounce check --app myapp:app                 # server preflight

See the deployment guide.

Benchmarks

Synthetic benchmarks comparing Chirp, FastHTML, FastAPI, Flask, Starlette, and Litestar:

uv sync --extra benchmark
uv run poe benchmark

See the committed baseline and full artifact for current results, caveats, environment metadata, and runners.


Development

git clone https://github.com/lbliii/chirp.git
cd chirp
make install          # once per worktree (.python-version → 3.14t, docs deps)
make test
make site-serve     # docs site at http://127.0.0.1:5173

New Conductor worktree? Same flow: make install then make site-serve (or cd site && ./bengal s). Python pin and free-threading env live in git (.python-version, config/python.env, site/bengal); only .venv is recreated.

Run commands from the repository root. If an ancestor directory has old multi-repo dependency overrides, clone Chirp outside that parent before running make install.

Docs-site details: site/AGENTS.md.


The Bengal Ecosystem

Python-native stack for 3.14t free-threading. Chirp is the web framework; packages like chirp-ui sit on top as optional companions.

ᓚᘏᗢ Bengal Static site generator Docs
∿∿ Purr Content runtime
⌁⌁ Chirp Web framework ← You are here Docs
ʘ chirp-ui Optional companion UI layer
=^..^= Pounce ASGI server Docs
)彡 Kida Template engine Docs
ฅᨐฅ Patitas Markdown parser Docs
⌾⌾⌾ Rosettes Syntax highlighter Docs
Zoomies QUIC / HTTP/3

License

MIT

About

⌁⌁ Chirp — HTML-first web framework for Python 3.14+ with streaming, SSE, and fragment rendering

Topics

Resources

License

Contributing

Security policy

Stars

5 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors