Skip to content

Commit 65e8ce7

Browse files
Berik AshimovBerik Ashimov
authored andcommitted
docs: refresh README with Tier 1/2/3 features + migrate repo URLs ashimov/HawkAPI → ashimov/Hawk
1 parent 86c6dab commit 65e8ce7

7 files changed

Lines changed: 201 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
189189
- Pyright strict mode compliance (0 errors)
190190
- MkDocs documentation site
191191

192-
[0.1.2]: https://github.com/ashimov/HawkAPI/compare/v0.1.1...v0.1.2
193-
[0.1.1]: https://github.com/ashimov/HawkAPI/compare/v0.1.0...v0.1.1
194-
[0.1.0]: https://github.com/ashimov/HawkAPI/releases/tag/v0.1.0
192+
[0.1.2]: https://github.com/ashimov/Hawk/compare/v0.1.1...v0.1.2
193+
[0.1.1]: https://github.com/ashimov/Hawk/compare/v0.1.0...v0.1.1
194+
[0.1.0]: https://github.com/ashimov/Hawk/releases/tag/v0.1.0

README.md

Lines changed: 191 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<p align="center">
2-
<img src="hawkapi.png" alt="HawkAPI" width="400">
2+
<img src="hawk-logo.png" alt="Hawk" width="400">
33
</p>
44

55
<p align="center">
66
<strong>High-performance Python web framework.</strong>
77
</p>
88

99
<p align="center">
10-
<a href="https://github.com/ashimov/HawkAPI/actions"><img src="https://github.com/ashimov/HawkAPI/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
10+
<a href="https://github.com/ashimov/Hawk/actions"><img src="https://github.com/ashimov/Hawk/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
1111
<a href="https://pypi.org/project/hawkapi/"><img src="https://img.shields.io/pypi/v/hawkapi.svg" alt="PyPI"></a>
1212
<a href="https://pypi.org/project/hawkapi/"><img src="https://img.shields.io/pypi/pyversions/hawkapi.svg" alt="Python"></a>
13-
<a href="https://github.com/ashimov/HawkAPI/blob/main/LICENSE"><img src="https://img.shields.io/github/license/ashimov/HawkAPI.svg" alt="License"></a>
14-
<a href="https://github.com/ashimov/HawkAPI"><img src="https://img.shields.io/badge/coverage-95%25-brightgreen.svg" alt="Coverage"></a>
13+
<a href="https://github.com/ashimov/Hawk/blob/main/LICENSE"><img src="https://img.shields.io/github/license/ashimov/Hawk.svg" alt="License"></a>
14+
<a href="https://github.com/ashimov/Hawk"><img src="https://img.shields.io/badge/coverage-95%25-brightgreen.svg" alt="Coverage"></a>
1515
<a href="https://hawkapi.ashimov.com"><img src="https://img.shields.io/badge/docs-hawkapi.ashimov.com-blue.svg" alt="Docs"></a>
1616
<a href="https://pypi.org/project/hawkapi/"><img src="https://img.shields.io/pypi/dm/hawkapi.svg" alt="Downloads"></a>
1717
<a href="benchmarks/competitive/RESULTS.md"><img src="https://img.shields.io/badge/benchmarks-5%2F6%20%F0%9F%8F%86-brightgreen.svg" alt="Benchmarks"></a>
@@ -67,14 +67,17 @@ pip install hawkapi
6767
With extras:
6868

6969
```bash
70-
pip install hawkapi[uvicorn] # ASGI server
71-
pip install hawkapi[pydantic] # Optional Pydantic v2 support
70+
pip install hawkapi[uvicorn] # Uvicorn ASGI server
7271
pip install hawkapi[granian] # Granian ASGI server
72+
pip install hawkapi[pydantic] # Optional Pydantic v2 support
7373
pip install hawkapi[otel] # OpenTelemetry tracing
74+
pip install hawkapi[metrics] # Prometheus metrics
75+
pip install hawkapi[logging] # Structured logging (structlog)
76+
pip install hawkapi[grpc] # gRPC support (grpcio + grpcio-reflection)
7477
pip install hawkapi[all] # Everything
7578
```
7679

77-
**Requirements:** Python 3.12+ and msgspec >= 0.19.0.
80+
**Requirements:** Python 3.12+ (3.13 fully supported; experimental free-threaded 3.13t wheels are shipped — see [Free-threaded Python](#free-threaded-python-313)) and msgspec >= 0.19.0.
7881

7982
---
8083

@@ -198,6 +201,21 @@ async def list_users(db: Annotated[Connection, Depends(get_db)]):
198201
return await db.fetch_all("SELECT * FROM users")
199202
```
200203

204+
#### Route-level Dependencies
205+
206+
Side-effect dependencies (auth guards, audit writers) that run before the handler — return values are discarded; `HTTPException` short-circuits the request:
207+
208+
```python
209+
from hawkapi import Depends
210+
211+
async def audit(request): ...
212+
213+
@app.get("/admin", dependencies=[Depends(audit)])
214+
async def admin(): ...
215+
```
216+
217+
Router-level is also supported: `Router(dependencies=[Depends(audit)])`.
218+
201219
#### DI Introspection
202220

203221
Inspect the container at runtime or export a Mermaid dependency graph:
@@ -246,6 +264,23 @@ async def get_user(user_id: int):
246264

247265
Works with both msgspec Structs and Pydantic models.
248266

267+
**Auto-inference from return annotation**`response_model` is inferred automatically when not set explicitly:
268+
269+
```python
270+
@app.get("/users/{id}")
271+
async def get_user(id: int) -> UserOut: # response_model inferred automatically
272+
return await db.get(id)
273+
```
274+
275+
**Exclusion flags** — strip `None`, unset, or default-value fields from serialized output:
276+
277+
```python
278+
@app.get("/users/{id}", response_model_exclude_none=True, response_model_exclude_unset=True)
279+
async def get_user(id: int) -> UserOut: ...
280+
```
281+
282+
Flags: `response_model_exclude_none`, `response_model_exclude_unset`, `response_model_exclude_defaults`.
283+
249284
### Pagination
250285

251286
Built-in offset and cursor pagination helpers:
@@ -294,6 +329,8 @@ class AuthMiddleware(Middleware):
294329
| `RedisRateLimitMiddleware` | Redis-backed token bucket rate limiting (distributed) |
295330
| `RequestLimitsMiddleware` | Max query string / header size enforcement |
296331
| `CircuitBreakerMiddleware` | Three-state circuit breaker (per-path tracking) |
332+
| `RedisCircuitBreakerMiddleware` | Distributed circuit breaker (Redis-backed) |
333+
| `AdaptiveConcurrencyMiddleware` | Adaptive in-flight limit based on latency |
297334
| `CSRFMiddleware` | Double-submit cookie CSRF protection |
298335
| `SessionMiddleware` | Signed cookie-based session management |
299336
| `ErrorHandlerMiddleware` | Structured error handling pipeline |
@@ -304,6 +341,8 @@ class AuthMiddleware(Middleware):
304341

305342
Middleware is registered globally via `app.add_middleware()`. Each entry is stored as a `MiddlewareEntry` dataclass holding the middleware class and its kwargs, then compiled into an onion-model pipeline at startup.
306343

344+
See also: [Bulkhead](#bulkhead) — named async concurrency isolator living in `hawkapi.middleware.bulkhead`.
345+
307346
### Security
308347

309348
```python
@@ -333,6 +372,26 @@ async def admin_panel():
333372
return {"secret": "data"}
334373
```
335374

375+
#### OAuth2 Scopes
376+
377+
Fine-grained scope enforcement with OpenAPI `operation.security` reflection:
378+
379+
```python
380+
from hawkapi import OAuth2PasswordBearer, Security, SecurityScopes
381+
382+
oauth2 = OAuth2PasswordBearer(
383+
tokenUrl="token",
384+
scopes={"items:read": "Read items", "items:write": "Write items"},
385+
)
386+
387+
async def current_user(scopes: SecurityScopes, token: str = Security(oauth2)): ...
388+
389+
@app.get("/items", dependencies=[Security(current_user, scopes=["items:read"])])
390+
async def read_items(): ...
391+
```
392+
393+
Route-level scopes are aggregated into the OpenAPI `operation.security` field automatically.
394+
336395
### Responses
337396

338397
```python
@@ -402,6 +461,18 @@ async def admin():
402461
)
403462
```
404463

464+
#### Status Constants
465+
466+
Use `hawkapi.status` for named HTTP and WebSocket constants (FastAPI parity):
467+
468+
```python
469+
from hawkapi import status
470+
471+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
472+
```
473+
474+
Both HTTP (`status.HTTP_200_OK`, `status.HTTP_422_UNPROCESSABLE_ENTITY`, …) and WebSocket (`status.WS_1008_POLICY_VIOLATION`, …) constants are available.
475+
405476
### Custom Exception Handlers
406477

407478
```python
@@ -522,6 +593,98 @@ app.mount("/site", StaticFiles(directory="public", html=True))
522593

523594
---
524595

596+
## Advanced Features
597+
598+
### Feature Flags
599+
600+
Runtime feature flags with zero external dependencies. Providers are swappable without restarting the app:
601+
602+
```python
603+
from hawkapi import HawkAPI, Depends
604+
from hawkapi.flags import StaticFlagProvider, Flags, get_flags, requires_flag
605+
606+
app = HawkAPI(flags=StaticFlagProvider({"new_checkout": True}))
607+
608+
@app.get("/checkout")
609+
async def checkout(flags: Flags = Depends(get_flags)) -> dict:
610+
if await flags.bool("new_checkout", default=False):
611+
return await new_flow()
612+
return await old_flow()
613+
614+
@app.get("/beta/reports")
615+
@requires_flag("beta.reports")
616+
async def beta_reports() -> dict: ...
617+
```
618+
619+
Providers: `StaticFlagProvider`, `EnvFlagProvider`, `FileFlagProvider` (JSON/TOML/YAML with mtime hot-reload). Use when you need to gate features per environment or roll out incrementally without a deployment. See [docs/guide/feature-flags.md](docs/guide/feature-flags.md).
620+
621+
### GraphQL
622+
623+
Thin GraphQL-over-HTTP adapter with GraphiQL UI served to browsers. Zero runtime deps on the default path — `graphql-core` and `strawberry` are imported lazily only when the adapter is used:
624+
625+
```python
626+
from hawkapi.graphql.adapters import from_graphql_core
627+
from graphql import build_schema
628+
629+
schema = build_schema("type Query { hello: String }")
630+
app.mount_graphql("/graphql", executor=from_graphql_core(schema))
631+
```
632+
633+
Adapters: `from_graphql_core`, `from_strawberry`. Use when you need a GraphQL API alongside REST without adding a separate framework. See [docs/guide/graphql.md](docs/guide/graphql.md).
634+
635+
### gRPC
636+
637+
Thin gRPC integration with ASGI lifespan-tied lifecycle — the gRPC server starts and stops with the app:
638+
639+
```python
640+
from myproto import greeter_pb2_grpc
641+
642+
class Greeter(greeter_pb2_grpc.GreeterServicer):
643+
async def SayHello(self, request, context):
644+
app = context.hawkapi_app # access app state
645+
...
646+
647+
app.mount_grpc(
648+
Greeter(),
649+
add_to_server=greeter_pb2_grpc.add_GreeterServicer_to_server,
650+
port=50051,
651+
reflection=True,
652+
reflection_service_names=["greeter.Greeter"],
653+
)
654+
```
655+
656+
Built-in `HawkAPIObservabilityInterceptor` adds structured logs and Prometheus metrics to every RPC. TLS via `ssl_credentials=`. Install: `pip install hawkapi[grpc]`. Use when you need gRPC and HTTP APIs from a single process. See [docs/guide/grpc.md](docs/guide/grpc.md).
657+
658+
### Bulkhead
659+
660+
Named async concurrency isolator — caps simultaneous in-flight requests per named pool to prevent one slow resource from exhausting the entire process:
661+
662+
```python
663+
from hawkapi.middleware.bulkhead import bulkhead
664+
665+
@app.get("/export")
666+
@bulkhead("exports", max_concurrent=4)
667+
async def export(): ...
668+
```
669+
670+
Context-manager form also supported. Backends: `LocalBulkheadBackend` (default, `asyncio.Semaphore` per name) and `RedisBulkheadBackend` (distributed, hash + lease-TTL). Optional Prometheus metrics. Use when you need hard concurrency caps per operation type. See [docs/guide/bulkhead.md](docs/guide/bulkhead.md).
671+
672+
### Free-threaded Python 3.13
673+
674+
HawkAPI ships experimental `cp313t-cp313t` wheels built via cibuildwheel for the no-GIL CPython 3.13 free-threaded build (PEP 703):
675+
676+
```python
677+
from hawkapi._threading import FREE_THREADED, maybe_thread_lock, maybe_async_lock
678+
679+
# FREE_THREADED is True only on a no-GIL 3.13t interpreter
680+
lock = maybe_thread_lock() # real threading.Lock on 3.13t, no-op elsewhere
681+
alock = maybe_async_lock() # real asyncio.Lock on 3.13t, no-op elsewhere
682+
```
683+
684+
`maybe_thread_lock()` and `maybe_async_lock()` let library code be PEP 703-aware without branching on every call site. Use when running HawkAPI under `python3.13t` for CPU-bound workloads that benefit from true parallelism. See [docs/guide/free-threaded.md](docs/guide/free-threaded.md).
685+
686+
---
687+
525688
## Production Features
526689

527690
### Health Probes
@@ -817,24 +980,38 @@ hawkapi dev app:app
817980
hawkapi dev app:app --host 0.0.0.0 --port 3000
818981

819982
# Detect API breaking changes
820-
hawkapi diff app:app --base openapi-v1.json
983+
hawkapi diff old_app:app new_app:app
821984

822985
# Lint OpenAPI spec
823986
hawkapi check app:app
824987

825988
# Generate API changelog
826-
hawkapi changelog app:app --base openapi-v1.json
989+
hawkapi changelog old_app:app new_app:app
827990

828991
# Scaffold a new project
829992
hawkapi new myproject
830993
hawkapi new myproject --docker
831994

832995
# Initialize config files in current directory
833996
hawkapi init
997+
998+
# Generate a TypeScript or Python client SDK from OpenAPI
999+
hawkapi gen-client --app app:app --lang typescript --out ./client
1000+
hawkapi gen-client --app app:app --lang python --out ./client
1001+
1002+
# Migrate a FastAPI codebase to HawkAPI (in-place rewrite)
1003+
hawkapi migrate path/to/fastapi_project
1004+
hawkapi migrate path/to/fastapi_project --dry-run # preview diffs only
1005+
hawkapi migrate path/to/fastapi_project --convert-models # also rewrite Pydantic BaseModel → msgspec.Struct
1006+
hawkapi migrate path/to/fastapi_project --output ./migrated # write to separate directory
8341007
```
8351008

8361009
`hawkapi init` creates `.env` and `.env.example` files with commented-out HawkAPI configuration templates. Existing files are skipped.
8371010

1011+
`hawkapi gen-client` generates a zero-dependency client: native `fetch` for TypeScript, msgspec-backed for Python. Source can be a live app (`--app module:attr`) or a saved spec file (`--spec openapi.json`).
1012+
1013+
`hawkapi migrate` uses AST rewriting (libcst) to replace FastAPI imports, decorators, and patterns with their HawkAPI equivalents. See [docs/guide/migration-from-fastapi.md](docs/guide/migration-from-fastapi.md).
1014+
8381015
---
8391016

8401017
## Configuration
@@ -905,6 +1082,8 @@ Response headers use `CaseInsensitiveDict` — lookups like `response.headers["c
9051082

9061083
## Benchmarks
9071084

1085+
> **Note:** The numbers below are local dev measurements on Apple M3 Pro. The canonical competitive comparison (Linux CI, head-to-head against FastAPI/Litestar/BlackSheep/Starlette/Sanic) is the table at the top of this README.
1086+
9081087
Tested on Apple M3 Pro, Python 3.13, msgspec 0.20. ASGI-level benchmarks (no HTTP server overhead — pure framework performance).
9091088

9101089
### Request/Response
@@ -947,11 +1126,11 @@ python benchmarks/bench_vs_fastapi.py
9471126
## Development
9481127

9491128
```bash
950-
git clone https://github.com/ashimov/HawkAPI.git
951-
cd HawkAPI
1129+
git clone https://github.com/ashimov/Hawk.git
1130+
cd Hawk
9521131
pip install -e ".[dev]"
9531132

954-
# Run tests (816 tests)
1133+
# Run tests (1211 tests)
9551134
pytest
9561135

9571136
# Lint and type check

docs/getting-started/installation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pip install "hawkapi[all]"
3030
## Development Install
3131

3232
```bash
33-
git clone https://github.com/ashimov/HawkAPI.git
33+
git clone https://github.com/ashimov/Hawk.git
3434
cd hawkapi
3535
pip install -e ".[dev]"
3636
```

docs/guide/benchmarks.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
HawkAPI competes head-to-head with the top Python ASGI frameworks on six
44
standardised scenarios. Numbers are regenerated automatically every Monday
55
and on every release — see the live
6-
[`benchmarks/competitive/RESULTS.md`](https://github.com/ashimov/HawkAPI/blob/main/benchmarks/competitive/RESULTS.md).
6+
[`benchmarks/competitive/RESULTS.md`](https://github.com/ashimov/Hawk/blob/main/benchmarks/competitive/RESULTS.md).
77

88
## Methodology
99

@@ -28,7 +28,7 @@ As of 2026-04-17 (first committed snapshot), HawkAPI leads throughput on
2828
within 11 % on that scenario.
2929

3030
See
31-
[`benchmarks/competitive/RESULTS.md`](https://github.com/ashimov/HawkAPI/blob/main/benchmarks/competitive/RESULTS.md)
31+
[`benchmarks/competitive/RESULTS.md`](https://github.com/ashimov/Hawk/blob/main/benchmarks/competitive/RESULTS.md)
3232
for the full throughput + p99 latency breakdown per run.
3333

3434
## Reproducing locally

hawkapi.png

-2.01 MB
Binary file not shown.

mkdocs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
site_name: HawkAPI
22
site_description: High-performance Python web framework
33
site_url: https://hawkapi.ashimov.com
4-
repo_url: https://github.com/ashimov/HawkAPI
5-
repo_name: ashimov/HawkAPI
4+
repo_url: https://github.com/ashimov/Hawk
5+
repo_name: ashimov/Hawk
66

77
theme:
88
name: material

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ build = [
7777
hawkapi = "hawkapi.cli:main"
7878

7979
[project.urls]
80-
Homepage = "https://github.com/ashimov/HawkAPI"
80+
Homepage = "https://github.com/ashimov/Hawk"
8181
Documentation = "https://hawkapi.ashimov.com"
82-
Repository = "https://github.com/ashimov/HawkAPI"
82+
Repository = "https://github.com/ashimov/Hawk"
8383

8484
[tool.hatch.build.targets.wheel]
8585
packages = ["src/hawkapi"]

0 commit comments

Comments
 (0)