{"id":44150452,"url":"https://github.com/lbliii/chirp","last_synced_at":"2026-03-10T18:10:25.907Z","repository":{"id":337089739,"uuid":"1152291210","full_name":"lbliii/chirp","owner":"lbliii","description":"⌁⌁ Chirp — HTML-first web framework for Python 3.14+ with streaming, SSE, and fragment rendering","archived":false,"fork":false,"pushed_at":"2026-03-04T03:27:06.000Z","size":1942,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-04T05:39:09.597Z","etag":null,"topics":["asgi","free-threading","html-over-the-wire","htmx","kida","nogil","python","python314","sse","streaming","web-framework"],"latest_commit_sha":null,"homepage":"https://lbliii.github.io/chirp/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lbliii.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-07T16:56:19.000Z","updated_at":"2026-03-04T00:26:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lbliii/chirp","commit_stats":null,"previous_names":["lbliii/chirp"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/lbliii/chirp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lbliii%2Fchirp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lbliii%2Fchirp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lbliii%2Fchirp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lbliii%2Fchirp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lbliii","download_url":"https://codeload.github.com/lbliii/chirp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lbliii%2Fchirp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30346628,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T15:55:29.454Z","status":"ssl_error","status_checked_at":"2026-03-10T15:54:58.440Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asgi","free-threading","html-over-the-wire","htmx","kida","nogil","python","python314","sse","streaming","web-framework"],"created_at":"2026-02-09T03:15:30.104Z","updated_at":"2026-03-10T18:10:25.898Z","avatar_url":"https://github.com/lbliii.png","language":"Python","readme":"# ⌁⌁ Chirp\n\n[![PyPI version](https://img.shields.io/pypi/v/bengal-chirp.svg)](https://pypi.org/project/bengal-chirp/)\n[![Python 3.14+](https://img.shields.io/badge/python-3.14+-blue.svg)](https://pypi.org/project/bengal-chirp/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![Status: Alpha](https://img.shields.io/badge/status-alpha-orange.svg)](https://pypi.org/project/bengal-chirp/)\n\n**A Python web framework for HTML over the wire.**\n\n```python\nfrom chirp import App\n\napp = App()\n\n@app.route(\"/\")\ndef index():\n    return \"Hello, World!\"\n\napp.run()\n```\n\n---\n\n## What is Chirp?\n\nChirp 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.\n\n**What's good about it:**\n\n- **Browser-native UI** — `\u003cdialog\u003e`, `popover`, View Transitions, container queries. Most of what required a JS framework is now native HTML and CSS.\n- **HTML over the wire** — Serve full pages, template fragments, streaming HTML, and SSE. Built for htmx and the modern browser.\n- **Streaming HTML** — Send the page shell immediately and fill in content as data becomes available. No loading spinners, no skeleton screens.\n- **Server-Sent Events** — Push real-time updates over plain HTTP. No WebSocket protocol upgrade, no special infrastructure.\n\n---\n\n## Installation\n\n```bash\n# pip\npip install bengal-chirp\n\n# uv\nuv add bengal-chirp\n```\n\nRequires Python 3.14+.\n\nChirp works on its own with plain templates. `chirp-ui` is an optional companion UI layer, not part of the framework core.\n\n---\n\n## Quick Start\n\n```bash\nchirp new myapp \u0026\u0026 cd myapp \u0026\u0026 python app.py\n```\n\n| Function | Description |\n|----------|-------------|\n| `chirp new \u003cname\u003e` | Scaffold an auth-ready project |\n| `chirp new \u003cname\u003e --shell` | Scaffold with a persistent app shell (topbar + sidebar) |\n| `chirp new \u003cname\u003e --sse` | Scaffold with SSE boilerplate (`EventStream`, `sse_scope`) |\n| `chirp run \u003capp\u003e` | Start the dev server from an import string |\n| `chirp check \u003capp\u003e` | Validate hypermedia contracts |\n| `chirp check \u003capp\u003e --warnings-as-errors` | Fail CI on contract warnings |\n| `chirp routes \u003capp\u003e` | Print the registered route table |\n| `App()` | Create an application |\n| `@app.route(path)` | Register a route handler |\n| `Template(name, **ctx)` | Render a full template |\n| `Template.inline(src, **ctx)` | Render from string (prototyping) |\n| `Page(name, block, **ctx)` | Auto Fragment or Template based on request |\n| `PageComposition(template, fragment_block, ...)` | Python-first composition with regions |\n| `Fragment(name, block, **ctx)` | Render a named template block |\n| `Stream(name, **ctx)` | Stream HTML progressively |\n| `Suspense(name, **ctx)` | Shell first, OOB swaps for deferred data |\n| `EventStream(gen)` | Server-Sent Events stream |\n| `hx_redirect(url)` | Redirect helper for htmx and full-page requests |\n| `app.run()` | Start the development server |\n\n---\n\n## Features\n\n| Feature | Description | Docs |\n|---------|-------------|------|\n| **Routing** | Pattern matching, path params, method dispatch | [Routing →](https://lbliii.github.io/chirp/docs/routing/) |\n| **Filesystem routing** | Route discovery from `pages/` with layouts | [Filesystem →](https://lbliii.github.io/chirp/docs/routing/filesystem-routing/) |\n| **Templates** | Kida integration, rendering, filters | [Templates →](https://lbliii.github.io/chirp/docs/templates/) |\n| **Fragments** | Render named template blocks independently | [Fragments →](https://lbliii.github.io/chirp/docs/templates/fragments/) |\n| **Forms** | `form_or_errors`, form macros, validation | [Forms →](https://lbliii.github.io/chirp/docs/data/forms-validation/) |\n| **Streaming** | Progressive HTML rendering via Kida | [Streaming →](https://lbliii.github.io/chirp/docs/streaming/) |\n| **SSE** | Server-Sent Events for real-time updates | [SSE →](https://lbliii.github.io/chirp/docs/streaming/server-sent-events/) |\n| **Middleware** | CORS, sessions, static files, security headers, custom | [Middleware →](https://lbliii.github.io/chirp/docs/middleware/) |\n| **Contracts** | Validate htmx attrs, form actions, and route-bearing dialog args | [Reference →](https://lbliii.github.io/chirp/docs/reference/) |\n| **Testing** | Test client, assertions, isolation utilities | [Testing →](https://lbliii.github.io/chirp/docs/testing/) |\n| **Data** | Database integration and form validation | [Data →](https://lbliii.github.io/chirp/docs/data/) |\n| **Optional UI layer** | `chirp-ui` companion components and styles | [chirp-ui →](https://github.com/lbliii/chirp-ui) |\n\n📚 **Full documentation**: [lbliii.github.io/chirp](https://lbliii.github.io/chirp/)\n\n---\n\n## Production Deployment\n\nChirp apps run on **[pounce](https://github.com/lbliii/pounce)**, a production-grade ASGI server with enterprise features built-in:\n\n### Automatic Features (Zero Configuration)\n- ✅ **WebSocket compression** — 60% bandwidth reduction\n- ✅ **HTTP/2 support** — Multiplexed streams, server push\n- ✅ **Graceful shutdown** — Finishes active requests on SIGTERM\n- ✅ **Zero-downtime reload** — `kill -SIGUSR1` for hot code updates\n- ✅ **Built-in health endpoint** — `/health` for Kubernetes probes\n\n### Production Features (Configurable)\n- 📊 **Prometheus metrics** — `/metrics` endpoint for monitoring\n- 🛡️ **Per-IP rate limiting** — Token bucket algorithm, configurable burst\n- 📦 **Request queueing** — Load shedding during traffic spikes\n- 🐛 **Sentry integration** — Automatic error tracking and reporting\n- 🔄 **Multi-worker mode** — CPU-based auto-scaling\n\n### Quick Start: Production Mode\n\n```python\nfrom chirp import App, AppConfig\n\n# Production configuration\nconfig = AppConfig(\n    debug=False,  # ← Enables production mode\n    workers=4,\n    metrics_enabled=True,\n    rate_limit_enabled=True,\n    sentry_dsn=\"https://...\",\n)\n\napp = App(config=config)\n\n@app.route(\"/\")\ndef index():\n    return \"Hello, Production!\"\n\napp.run()  # ← Automatically uses production server\n```\n\n### CLI Production Mode\n\n```bash\n# Development (single worker, auto-reload)\nchirp run myapp:app\n\n# Production (multi-worker, all features)\nchirp run myapp:app --production --workers 4 --metrics --rate-limit\n```\n\n### Docker Deployment\n\n```dockerfile\nFROM python:3.14-slim\nWORKDIR /app\nCOPY . .\nRUN pip install bengal-chirp\nCMD [\"chirp\", \"run\", \"myapp:app\", \"--production\", \"--workers\", \"4\"]\n```\n\n**Full deployment guide**: [Deployment docs](https://lbliii.github.io/chirp/docs/deployment/production/)\n\n---\n\n## Usage\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eReturn Values\u003c/strong\u003e — Type-driven content negotiation\u003c/summary\u003e\n\nRoute functions return *values*. The framework handles content negotiation based on the type:\n\n```python\nreturn \"Hello\"                                  # -\u003e 200, text/html\nreturn {\"users\": [...]}                         # -\u003e 200, application/json\nreturn Template(\"page.html\", title=\"Home\")      # -\u003e 200, rendered via Kida\nreturn Page(\"search.html\", \"results\", items=x)  # -\u003e Fragment or Template (auto)\nreturn Fragment(\"page.html\", \"results\", items=x) # -\u003e 200, rendered block\nreturn Stream(\"dashboard.html\", **async_ctx)    # -\u003e 200, streamed HTML\nreturn Suspense(\"dashboard.html\", stats=...)    # -\u003e shell + OOB swaps\nreturn EventStream(generator())                 # -\u003e SSE stream\nreturn hx_redirect(\"/dashboard\")                # -\u003e Location + HX-Redirect\nreturn Response(body=b\"...\", status=201)         # -\u003e explicit control\nreturn Redirect(\"/login\")                       # -\u003e 302\n```\n\nNo `make_response()`. No `jsonify()`. The type *is* the intent.\n\nFor htmx-driven form posts or mutations that should trigger a full-page\nnavigation, prefer `hx_redirect()` so both plain browser and htmx requests\nfollow the redirect correctly.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eFragments and htmx\u003c/strong\u003e — Render template blocks independently\u003c/summary\u003e\n\nKida can render a named block from a template independently, without rendering the whole page:\n\n```html\n{# templates/search.html #}\n{% extends \"base.html\" %}\n\n{% block content %}\n  \u003cinput type=\"search\" hx-get=\"/search\" hx-target=\"#results\" name=\"q\"\u003e\n  {% block results_list %}\n    \u003cdiv id=\"results\"\u003e\n      {% for item in results %}\n        \u003cdiv class=\"result\"\u003e{{ item.title }}\u003c/div\u003e\n      {% end %}\n    \u003c/div\u003e\n  {% endblock %}\n{% endblock %}\n```\n\n```python\n@app.route(\"/search\")\nasync def search(request: Request):\n    results = await db.search(request.query.get(\"q\", \"\"))\n    if request.is_fragment:\n        return Fragment(\"search.html\", \"results_list\", results=results)\n    return Template(\"search.html\", results=results)\n```\n\nFull page request renders everything. htmx request renders just the `results_list` block.\nSame template, same data, different scope. No separate \"partials\" directory.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eStreaming HTML\u003c/strong\u003e — Progressive rendering\u003c/summary\u003e\n\nKida renders template sections as they complete. The browser receives the shell immediately\nand content fills in progressively:\n\n```python\n@app.route(\"/dashboard\")\nasync def dashboard(request: Request):\n    return Stream(\"dashboard.html\",\n        header=site_header(),\n        stats=await load_stats(),\n        activity=await load_activity(),\n    )\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eServer-Sent Events\u003c/strong\u003e — Real-time HTML updates\u003c/summary\u003e\n\nPush Kida-rendered HTML fragments to the browser in real-time:\n\n```python\n@app.route(\"/notifications\")\nasync def notifications(request: Request):\n    async def stream():\n        async for event in notification_bus.subscribe(request.user):\n            yield Fragment(\"components/notification.html\", event=event)\n    return EventStream(stream())\n```\n\nCombined with htmx's SSE support, this enables real-time UI updates with zero client-side\nJavaScript. The server renders HTML, the browser swaps it in.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eMiddleware\u003c/strong\u003e — Composable request/response pipeline\u003c/summary\u003e\n\nNo base class. No inheritance. A middleware is anything that matches the protocol:\n\n```python\nasync def timing(request: Request, next: Next) -\u003e Response:\n    start = time.monotonic()\n    response = await next(request)\n    elapsed = time.monotonic() - start\n    return response.with_header(\"X-Time\", f\"{elapsed:.3f}\")\n\napp.add_middleware(timing)\n```\n\nBuilt-in middleware: CORS, StaticFiles, HTMLInject, Sessions, SecurityHeaders.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eTyped Contracts\u003c/strong\u003e — Compile-time hypermedia validation\u003c/summary\u003e\n\nChirp validates the server-client boundary at startup:\n\n```python\n# Prints a contract report and exits non-zero on errors.\napp.check()\n\n# Optional strict mode: treat warnings as failures too.\napp.check(warnings_as_errors=True)\n```\n\nEvery `hx-get`, `hx-post`, and `action` attribute in your templates is checked against the\nregistered route table. Every `Fragment` and `SSE` return type is checked against available\ntemplate blocks. SSE safety checks catch broken `sse-connect` / `sse-swap` structures and\nunsafe inherited target scopes before runtime.\n\nFor strict CI:\n\n```bash\nchirp check myapp:app --warnings-as-errors\n```\n\n\u003c/details\u003e\n\n---\n\n## Key Ideas\n\n- **HTML over the wire.** Serve full pages, template fragments, streaming HTML, and\n  Server-Sent Events. Built for htmx and the modern browser.\n- **Kida built in.** Same author, no seam. Fragment rendering, streaming templates, and\n  filter registration are first-class features, not afterthoughts.\n- **Typed end-to-end.** Frozen config, frozen request, chainable response. Zero\n  `type: ignore` comments.\n- **Free-threading native.** Designed for Python 3.14t from the first line. Immutable data\n  structures, ContextVar isolation.\n- **Contracts, not conventions.** `app.check()` validates the full hypermedia surface at\n  startup.\n- **UI is optional.** Build with plain templates and your own design system, or add\n  `chirp-ui` as a companion layer.\n- **Minimal dependencies.** `kida-templates` + `anyio` + `bengal-pounce`. Everything else is optional.\n\n---\n\n## Documentation\n\n📚 **[lbliii.github.io/chirp](https://lbliii.github.io/chirp/)**\n\n| Section | Description |\n|---------|-------------|\n| [Get Started](https://lbliii.github.io/chirp/docs/get-started/) | Installation and quickstart |\n| [Core Concepts](https://lbliii.github.io/chirp/docs/core-concepts/) | App lifecycle, return values, configuration |\n| [Routing](https://lbliii.github.io/chirp/docs/routing/) | Routes, filesystem routing, requests |\n| [Templates](https://lbliii.github.io/chirp/docs/templates/) | Rendering, fragments, filters |\n| [Streaming](https://lbliii.github.io/chirp/docs/streaming/) | HTML streaming and Server-Sent Events |\n| [Middleware](https://lbliii.github.io/chirp/docs/middleware/) | Built-in and custom middleware |\n| [Data](https://lbliii.github.io/chirp/docs/data/) | Database integration and forms |\n| [Testing](https://lbliii.github.io/chirp/docs/testing/) | Test client and assertions |\n| [Deployment](https://lbliii.github.io/chirp/docs/deployment/) | Production deployment with Pounce |\n| [Guides](https://lbliii.github.io/chirp/docs/guides/) | App shells, islands, Alpine, accessibility |\n| [Tutorials](https://lbliii.github.io/chirp/docs/tutorials/) | Flask migration, htmx patterns |\n| [Examples](https://lbliii.github.io/chirp/docs/examples/) | RAG demo, production stack, API |\n| [Reference](https://lbliii.github.io/chirp/docs/reference/) | API documentation |\n\n---\n\n## Development\n\n```bash\ngit clone https://github.com/lbliii/chirp.git\ncd chirp\nuv sync --group dev\npytest\n```\n\n---\n\n## The Bengal Ecosystem\n\nA 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.\n\n| | | | |\n|--:|---|---|---|\n| **ᓚᘏᗢ** | [Bengal](https://github.com/lbliii/bengal) | Static site generator | [Docs](https://lbliii.github.io/bengal/) |\n| **∿∿** | [Purr](https://github.com/lbliii/purr) | Content runtime | — |\n| **⌁⌁** | **Chirp** | Web framework ← You are here | [Docs](https://lbliii.github.io/chirp/) |\n| **ʘ** | [chirp-ui](https://github.com/lbliii/chirp-ui) | Optional companion UI layer | — |\n| **=^..^=** | [Pounce](https://github.com/lbliii/pounce) | ASGI server | [Docs](https://lbliii.github.io/pounce/) |\n| **)彡** | [Kida](https://github.com/lbliii/kida) | Template engine | [Docs](https://lbliii.github.io/kida/) |\n| **ฅᨐฅ** | [Patitas](https://github.com/lbliii/patitas) | Markdown parser | [Docs](https://lbliii.github.io/patitas/) |\n| **⌾⌾⌾** | [Rosettes](https://github.com/lbliii/rosettes) | Syntax highlighter | [Docs](https://lbliii.github.io/rosettes/) |\n\nPython-native. Free-threading ready. No npm required.\n\n---\n\n## License\n\nMIT\n","funding_links":[],"categories":["Micro-frameworks"],"sub_categories":["Async"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flbliii%2Fchirp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flbliii%2Fchirp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flbliii%2Fchirp/lists"}