Metadata-Version: 2.4
Name: blackbull
Version: 0.31.3
Summary: Async ASGI 3.0 framework with a from-scratch HTTP/1.1 parser, HTTP/2 frame layer, and WebSocket codec. HPACK delegates to the hpack library.
Author: TOKUJI
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/TOKUJI/BlackBull
Project-URL: Documentation, https://tokuji.github.io/BlackBull/
Project-URL: Repository, https://github.com/TOKUJI/BlackBull
Project-URL: Changelog, https://github.com/TOKUJI/BlackBull/blob/master/CHANGELOG.md
Project-URL: Issues, https://github.com/TOKUJI/BlackBull/issues
Keywords: asgi,asyncio,http,http2,websocket,web,framework,server
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: hpack
Requires-Dist: beartype
Provides-Extra: compression
Requires-Dist: brotli; extra == "compression"
Requires-Dist: zstandard; extra == "compression"
Provides-Extra: validation
Provides-Extra: testing
Requires-Dist: pytest; extra == "testing"
Requires-Dist: pytest-asyncio; extra == "testing"
Requires-Dist: pytest-timeout; extra == "testing"
Requires-Dist: pytest-cov; extra == "testing"
Requires-Dist: beartype; extra == "testing"
Requires-Dist: pytest-beartype; extra == "testing"
Requires-Dist: httpx[http2]; extra == "testing"
Requires-Dist: websockets; extra == "testing"
Requires-Dist: hypothesis; extra == "testing"
Provides-Extra: docs
Requires-Dist: mkdocs-material; extra == "docs"
Requires-Dist: mkdocstrings[python]; extra == "docs"
Requires-Dist: mkdocs-gen-files; extra == "docs"
Requires-Dist: mkdocs-literate-nav; extra == "docs"
Requires-Dist: mkdocs-autorefs; extra == "docs"
Provides-Extra: speed
Requires-Dist: uvloop; extra == "speed"
Provides-Extra: reload
Requires-Dist: watchfiles; extra == "reload"
Provides-Extra: benchmark
Provides-Extra: profiling
Requires-Dist: py-spy; extra == "profiling"
Dynamic: license-file

# BlackBull

> ⚠ **Early Alpha — API may break between MINOR versions.**
> Conformance evidence: [`docs/about/conformance.md`](https://github.com/TOKUJI/BlackBull/blob/master/docs/about/conformance.md).
> Things to know before adopting: [`KNOWN_LIMITATIONS.md`](https://github.com/TOKUJI/BlackBull/blob/master/KNOWN_LIMITATIONS.md).

**Async ASGI 3.0 framework** with a from-scratch HTTP/1.1 parser,
HTTP/2 frame layer, and WebSocket codec — no `httptools`, no
`uvicorn`, no `hypercorn` underneath.  HPACK header compression
delegates to the [`hpack`](https://github.com/python-hyper/hpack)
library (re-implementing a conformant HPACK encoder/decoder is a
project of its own); everything else, including the ASGI server,
event loop integration, prioritisation, push, flow control, and
stream actor, is BlackBull's own code.  Single deployable, no
third-party C extensions in the protocol stack.

[![PyPI](https://img.shields.io/pypi/v/blackbull.svg)](https://pypi.org/project/blackbull/)
[![Python](https://img.shields.io/pypi/pyversions/blackbull.svg)](https://pypi.org/project/blackbull/)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)

## Why BlackBull

- **One package, one process** — the framework *is* the server.  No
  separate ASGI runner; `app.run()` opens the socket and serves.
- **From-scratch protocol stack** for HTTP/1.1 (RFC 9112) parser,
  HTTP/2 (RFC 9113) frame layer + flow control + RFC 9218
  prioritisation + push, and WebSocket (RFC 6455) codec.  HPACK
  (RFC 7541) is the one exception — see headline above.
- **Pure-Python identity** — no `httptools`, no `uvloop` dependency
  (uvloop available as an optional `[speed]` extra).  The single
  third-party dependency in the protocol stack is `hpack`, also
  pure Python.
- **Conformance-tested** against `h2spec`, Autobahn, and a
  differential nginx fuzz corpus.
- **Modern Python** — requires 3.11+, full type hints, PEP 561 typed
  distribution.

## Install

```bash
pip install blackbull
pip install 'blackbull[compression]'   # add brotli + zstandard codecs
pip install 'blackbull[speed]'         # add uvloop event loop
pip install 'blackbull[reload]'        # add watchfiles for --reload
```

## Hello, world

```python
from blackbull import BlackBull

app = BlackBull()

@app.route(path='/')
async def hello():
    return "Hello, world!"

if __name__ == '__main__':
    app.run(port=8000)
```

Run it:

```bash
python app.py              # HTTP/1.1 on :8000
```

Or via the bundled CLI:

```bash
blackbull app:app --bind 0.0.0.0:8000
```

## Simplified handlers

Route handlers may return a `str`, `bytes`, `dict`, or `Response`;
path parameters are coerced to the annotation type:

```python
@app.route(path='/tasks/{task_id:int}')
async def get_task(task_id: int):
    return {"id": task_id, "title": "..."}
```

Drop down to full ASGI `(scope, receive, send)` whenever you need it
— routes accept either shape.

## TLS + HTTP/2

```python
app.run(port=8443, certfile='cert.pem', keyfile='key.pem')
```

ALPN negotiates `h2` automatically; HTTP/1.1 clients fall back via
the same socket.

## WebSocket

```python
from http import HTTPMethod
from blackbull.utils import Scheme

@app.route(path='/ws', methods=[HTTPMethod.GET], scheme=Scheme.websocket)
async def ws_echo(scope, receive, send):
    await receive()                              # websocket.connect
    await send({'type': 'websocket.accept'})
    while True:
        msg = await receive()
        if msg['type'] == 'websocket.disconnect':
            break
        if msg['type'] == 'websocket.receive':
            await send({'type': 'websocket.send',
                        'text': msg.get('text') or ''})
```

## Built-in middleware

Compose via `app.use(...)` or per-route `middlewares=[...]`:

| Middleware       | What it does |
|------------------|---|
| `Compression`    | Negotiates `br` / `zstd` / `gzip` from `Accept-Encoding` |
| `StaticFiles`    | Serves files from a directory under a URL prefix |
| `Cache`          | Per-worker LRU + ETag / `Cache-Control` honouring |
| `Session`        | Signed-cookie sessions (HMAC-SHA256) |
| `CORS`           | Preflight + actual-request header injection |
| `TrustedProxy`   | Rewrites `scope['client']` / `scope['scheme']` from proxy headers |

## OpenAPI / Swagger UI

```python
app.enable_openapi()   # publishes /openapi.json and /docs
```

Auto-generates an OpenAPI 3.1 spec from route signatures, path-param
converters, docstrings, and `@dataclass` annotations on body
parameters.  Dataclass-typed bodies are also deserialized at runtime
— `async def h(body: CreateTask): ...` receives a constructed
instance, no manual `json.loads`.

## Examples

| Example | Demonstrates |
|---|---|
| [`examples/SimpleTaskManager/`](examples/SimpleTaskManager/) | REST API + HTML UI, middleware pipeline, route groups, SQLite, Bearer token auth |
| [`examples/ChatServer/`](examples/ChatServer/) | WebSocket, SSE, long polling side by side; Session + Compression + custom auth |
| [`examples/typed_routes_ok.py`](examples/typed_routes_ok.py) | `{param:converter}` syntax, `url_path_for` |

## Documentation

- **Guide**: [`docs/guide.md`](https://github.com/TOKUJI/BlackBull/blob/master/docs/guide.md)
- **Architecture**: [`docs/ActorDesign.md`](https://github.com/TOKUJI/BlackBull/blob/master/docs/ActorDesign.md)
- **Changelog**: [`CHANGELOG.md`](https://github.com/TOKUJI/BlackBull/blob/master/CHANGELOG.md)

## Versioning

BlackBull uses [ZeroVer](https://0ver.org/) prior to a 1.0 commitment.
`MINOR` advances at each sprint close; `PATCH` is for bug fixes and
harness work between sprints.  See [`CHANGELOG.md`](https://github.com/TOKUJI/BlackBull/blob/master/CHANGELOG.md) for
the full release history.

## License

[Apache License 2.0](LICENSE) — © TOKUJI.
