Skip to main content

A Python web framework for the modern web platform — HTML fragments, streaming, SSE, free-threading ready

Project description

⌁⌁ Chirp

PyPI version Python 3.14+ License: MIT Status: Alpha

A Python web framework for HTML over the wire.

from chirp import App

app = App()

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

app.run()

What is Chirp?

Chirp is a Python web framework built for the modern web platform: browser-native UI, HTML over the wire, streaming responses, and Server-Sent Events. Use plain Kida templates, your own CSS, or optional companion packages like chirp-ui. Return values drive content negotiation — no make_response(), no jsonify(). The type is the intent.

What's good about it:

  • Browser-native UI<dialog>, popover, View Transitions, container queries. Most of what required a JS framework is now native HTML and CSS.
  • HTML over the wire — Serve full pages, template fragments, streaming HTML, and SSE. Built for htmx and the modern browser.
  • Streaming HTML — Send the page shell immediately and fill in content as data becomes available. No loading spinners, no skeleton screens.
  • Server-Sent Events — Push real-time updates over plain HTTP. No WebSocket protocol upgrade, no special infrastructure.

Installation

# pip
pip install bengal-chirp

# uv
uv add bengal-chirp

Requires Python 3.14+.

Chirp works on its own with plain templates. chirp-ui is an optional companion UI layer, not part of the framework core.


Quick Start

chirp new myapp && cd myapp && python app.py
Function Description
chirp new <name> Scaffold an auth-ready project
chirp new <name> --shell Scaffold with a persistent app shell (topbar + sidebar)
chirp new <name> --sse Scaffold with SSE boilerplate (EventStream, sse_scope)
chirp run <app> Start the dev server from an import string
chirp check <app> Validate hypermedia contracts
chirp check <app> --warnings-as-errors Fail CI on contract warnings
chirp routes <app> Print the registered route table
App() Create an application
@app.route(path) Register a route handler
Template(name, **ctx) Render a full template
Template.inline(src, **ctx) Render from string (prototyping)
Page(name, block, **ctx) Auto Fragment or Template based on request
PageComposition(template, fragment_block, ...) Python-first composition with regions
Fragment(name, block, **ctx) Render a named template block
Stream(name, **ctx) Stream HTML progressively
Suspense(name, **ctx) Shell first, OOB swaps for deferred data
EventStream(gen) Server-Sent Events stream
hx_redirect(url) Redirect helper for htmx and full-page requests
app.run() Start the development server

Features

Feature Description Docs
Routing Pattern matching, path params, method dispatch Routing →
Filesystem routing Route discovery from pages/ with layouts Filesystem →
Templates Kida integration, rendering, filters Templates →
Fragments Render named template blocks independently Fragments →
Forms form_or_errors, form macros, validation Forms →
Streaming Progressive HTML rendering via Kida Streaming →
SSE Server-Sent Events for real-time updates SSE →
Middleware CORS, sessions, static files, security headers, custom Middleware →
Contracts Validate htmx attrs, form actions, and route-bearing dialog args Reference →
Testing Test client, assertions, isolation utilities Testing →
Data Database integration and form validation Data →
Optional UI layer chirp-ui companion components and styles chirp-ui →

📚 Full documentation: lbliii.github.io/chirp


Production Deployment

Chirp apps run on pounce, a production-grade ASGI server with enterprise features built-in:

Automatic Features (Zero Configuration)

  • WebSocket compression — 60% bandwidth reduction
  • HTTP/2 support — Multiplexed streams, server push
  • Graceful shutdown — Finishes active requests on SIGTERM
  • Zero-downtime reloadkill -SIGUSR1 for hot code updates
  • Built-in health endpoint/health for Kubernetes probes

Production Features (Configurable)

  • 📊 Prometheus metrics/metrics endpoint for monitoring
  • 🛡️ Per-IP rate limiting — Token bucket algorithm, configurable burst
  • 📦 Request queueing — Load shedding during traffic spikes
  • 🐛 Sentry integration — Automatic error tracking and reporting
  • 🔄 Multi-worker mode — CPU-based auto-scaling

Quick Start: Production Mode

from chirp import App, AppConfig

# Production configuration
config = AppConfig(
    debug=False,  # ← Enables production mode
    workers=4,
    metrics_enabled=True,
    rate_limit_enabled=True,
    sentry_dsn="https://...",
)

app = App(config=config)

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

app.run()  # ← Automatically uses production server

CLI Production Mode

# Development (single worker, auto-reload)
chirp run myapp:app

# Production (multi-worker, all features)
chirp run myapp:app --production --workers 4 --metrics --rate-limit

Docker Deployment

FROM python:3.14-slim
WORKDIR /app
COPY . .
RUN pip install bengal-chirp
CMD ["chirp", "run", "myapp:app", "--production", "--workers", "4"]

Full deployment guide: Deployment docs


Usage

Return Values — Type-driven content negotiation

Route functions return values. The framework handles content negotiation based on the type:

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

No make_response(). No jsonify(). The type is the intent.

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

Fragments and htmx — Render template blocks independently

Kida can render a named block from a template independently, without rendering the whole page:

{# 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)

Full page request renders everything. htmx request renders just the results_list block. Same template, same data, different scope. No separate "partials" directory.

Streaming HTML — Progressive rendering

Kida renders template sections as they complete. The browser receives the shell immediately and content fills in progressively:

@app.route("/dashboard")
async def dashboard(request: Request):
    return Stream("dashboard.html",
        header=site_header(),
        stats=await load_stats(),
        activity=await load_activity(),
    )
Server-Sent Events — Real-time HTML updates

Push Kida-rendered HTML fragments to the browser in real-time:

@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, this enables real-time UI updates with zero client-side JavaScript. The server renders HTML, the browser swaps it in.

Middleware — Composable request/response pipeline

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.

Typed Contracts — Compile-time hypermedia validation

Chirp validates the server-client boundary at startup:

# Prints a contract report and exits non-zero on errors.
app.check()

# Optional strict mode: treat warnings as failures too.
app.check(warnings_as_errors=True)

Every hx-get, hx-post, and action attribute in your templates is checked against the registered route table. Every Fragment and SSE return type is checked against available template blocks. SSE safety checks catch broken sse-connect / sse-swap structures and unsafe inherited target scopes before runtime.

For strict CI:

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

Key Ideas

  • HTML over the wire. Serve full pages, template fragments, streaming HTML, and Server-Sent Events. Built for htmx and the modern browser.
  • Kida built in. Same author, no seam. Fragment rendering, streaming templates, and filter registration are first-class features, not afterthoughts.
  • Typed end-to-end. Frozen config, frozen request, chainable response. Zero type: ignore comments.
  • Free-threading native. Designed for Python 3.14t from the first line. Immutable data structures, ContextVar isolation.
  • Contracts, not conventions. app.check() validates the full hypermedia surface at startup.
  • UI is optional. Build with plain templates and your own design system, or add chirp-ui as a companion layer.
  • Minimal dependencies. kida-templates + anyio + bengal-pounce. Everything else is optional.

Documentation

📚 lbliii.github.io/chirp

Section Description
Get Started Installation and quickstart
Core Concepts App lifecycle, return values, configuration
Routing Routes, filesystem routing, requests
Templates Rendering, fragments, filters
Streaming HTML streaming and Server-Sent Events
Middleware Built-in and custom middleware
Data Database integration and forms
Testing Test client and assertions
Deployment Production deployment with Pounce
Guides App shells, islands, Alpine, accessibility
Tutorials Flask migration, htmx patterns
Examples RAG demo, production stack, API
Reference API documentation

Development

git clone https://github.com/lbliii/chirp.git
cd chirp
uv sync --group dev
pytest

The Bengal Ecosystem

A structured reactive stack written in pure Python for 3.14t free-threading. Chirp is the 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

Python-native. Free-threading ready. No npm required.


License

MIT

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

bengal_chirp-0.1.8.tar.gz (320.8 kB view details)

Uploaded Source

Built Distribution

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

bengal_chirp-0.1.8-py3-none-any.whl (255.5 kB view details)

Uploaded Python 3

File details

Details for the file bengal_chirp-0.1.8.tar.gz.

File metadata

  • Download URL: bengal_chirp-0.1.8.tar.gz
  • Upload date:
  • Size: 320.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for bengal_chirp-0.1.8.tar.gz
Algorithm Hash digest
SHA256 2e12ce479b42886ef9b8d43eee5186abadbc47f4c8ec6019a6344849656f8d78
MD5 60b1b67cdd0dabb80fe4df30f1a6e264
BLAKE2b-256 ed62b426e3ba4d20a6431aff8e0db375dc3f74a585352ca2a1535cea849da9c4

See more details on using hashes here.

Provenance

The following attestation bundles were made for bengal_chirp-0.1.8.tar.gz:

Publisher: python-publish.yml on lbliii/chirp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file bengal_chirp-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: bengal_chirp-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 255.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for bengal_chirp-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 69049082d6114f71ca4f40e6b4a2205e5ba1acc1070aef4a810396a0eea1b448
MD5 faba52d3b74930ecac5bb0575a534816
BLAKE2b-256 c67c22416eb01b5419e9a9e9f8e0274ad2d637c9c8566f28bc73605b3175c0fb

See more details on using hashes here.

Provenance

The following attestation bundles were made for bengal_chirp-0.1.8-py3-none-any.whl:

Publisher: python-publish.yml on lbliii/chirp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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