fenrir-framework 2.2.2__tar.gz → 2.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fenrir-framework might be problematic. Click here for more details.
- {fenrir_framework-2.2.2/fenrir_framework.egg-info → fenrir_framework-2.3.3}/PKG-INFO +200 -8
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/README.md +199 -7
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/__init__.py +12 -2
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/app.py +40 -22
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/cli.py +15 -28
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/compat.py +1 -1
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/context.py +0 -4
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/falcon.py +0 -1
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/helpers.py +1 -2
- fenrir_framework-2.3.3/fenrir/http2.py +136 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/middleware.py +54 -8
- fenrir_framework-2.3.3/fenrir/pool.py +269 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/request.py +29 -7
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/routing.py +99 -2
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/security.py +71 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/templating.py +1 -3
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/views.py +2 -2
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3/fenrir_framework.egg-info}/PKG-INFO +200 -8
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir_framework.egg-info/SOURCES.txt +3 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/pyproject.toml +1 -1
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_cli.py +3 -7
- fenrir_framework-2.3.3/tests/test_v3_features.py +595 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/LICENSE +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/background.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/bottle.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/config.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/dependencies.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/exceptions.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/json.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/logo.jpg +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/logo.png +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/openapi.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/pagination.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/response.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/sanic.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/sessions.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/signals.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/sse.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/testing.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/upload.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/websocket.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir_framework.egg-info/dependency_links.txt +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir_framework.egg-info/entry_points.txt +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir_framework.egg-info/requires.txt +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir_framework.egg-info/top_level.txt +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/setup.cfg +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_appctx.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_async.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_basic.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_blueprints.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_circular_deps.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_config.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_context.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_converters.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_custom_template.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_dep_overrides.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_dependencies.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_falcon_compat.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_form_file.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_helpers.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_instance_config.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_json.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_json_tag.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_logging.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_logo_route.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_middleware.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_new_features.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_new_middleware_features.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_regression.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_reqctx.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_request.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_resources.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_router_circular.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_routing.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_sanic_compat.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_security.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_session_interface.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_signals.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_sse.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_strict_content_type.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_subclassing.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_templating.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_testing.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_user_error_handler.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_validation.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_views.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_websocket.py +0 -0
- {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_yield_deps.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fenrir-framework
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.3
|
|
4
4
|
Summary: A hybrid Python web framework combining Flask, FastAPI, Bottle, Falcon, and Sanic
|
|
5
5
|
Requires-Python: >=3.8
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -24,11 +24,11 @@ Dynamic: license-file
|
|
|
24
24
|
[](https://pypi.org/project/fenrir-framework/)
|
|
25
25
|
[](https://opensource.org/licenses/MIT)
|
|
26
26
|
[](https://www.python.org/)
|
|
27
|
-
[](https://github.com/IshikawaUta/fenrir/actions)
|
|
28
28
|
[](https://github.com/IshikawaUta/fenrir/actions/workflows/test.yml)
|
|
29
29
|
[]()
|
|
30
30
|
|
|
31
|
-
**Fenrir** is a state-of-the-art, high-performance, hybrid Python web framework built on top of modern ASGI specifications. It elegantly merges the best programming paradigms from Python's most popular web frameworks (**Flask**, **FastAPI**, **Sanic**, **Falcon**, and **Bottle**) into a single unified workspace, powered locally by the premium **Asteri
|
|
31
|
+
**Fenrir** is a state-of-the-art, high-performance, hybrid Python web framework built on top of modern ASGI specifications. It elegantly merges the best programming paradigms from Python's most popular web frameworks (**Flask**, **FastAPI**, **Sanic**, **Falcon**, and **Bottle**) into a single unified workspace, powered locally by the premium **Asteri** application server.
|
|
32
32
|
|
|
33
33
|
Whether you prefer the automatic Pydantic validation of FastAPI, the seamless context-locals of Flask, the raw class-based speed of Falcon, or the robust background task model of Sanic, **Fenrir** allows you to leverage them all simultaneously in the same codebase.
|
|
34
34
|
|
|
@@ -42,6 +42,12 @@ Install directly from **PyPI**:
|
|
|
42
42
|
pip install fenrir-framework
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
Or install with Redis support for distributed sessions and rate limiting:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install fenrir-framework[redis]
|
|
49
|
+
```
|
|
50
|
+
|
|
45
51
|
Or install in development mode by cloning the repository:
|
|
46
52
|
|
|
47
53
|
```bash
|
|
@@ -55,6 +61,7 @@ pip install -e .
|
|
|
55
61
|
## 🌟 Key Features
|
|
56
62
|
|
|
57
63
|
* **⚡ High-Speed ASGI Core**: Extremely low-overhead routing and handler pipeline, achieving massive request throughput.
|
|
64
|
+
* **🔺 Trie-Based Routing**: O(k) route matching where k = path depth, instead of O(n) linear scan. Handles 1000+ routes efficiently.
|
|
58
65
|
* **🧩 Framework Hybridization**:
|
|
59
66
|
* **FastAPI Paradigm**: Native Pydantic v2 data validation, `Annotated` type decorators, automated parameter resolution (`Query`, `Path`, `Header`, `Cookie`, `Body`), dynamic dependency injection (`Depends`), and automated `response_model` serialization.
|
|
60
67
|
* **Flask Paradigm**: Thread/Task-safe context locals (`request`, `g`, `session`), Jinja2 template rendering (`render_template`), and request teardown hooks.
|
|
@@ -62,7 +69,13 @@ pip install -e .
|
|
|
62
69
|
* **Sanic Paradigm**: Global `sys.modules` patching (`install_sanic_compat()`), standard response helpers (`json`, `text`, `html`, `raw`, `redirect`), lifecycle listeners (`before_server_start`, etc.), and a background event scheduler (`app.add_task`).
|
|
63
70
|
* **Bottle Paradigm**: Built-in WSGI-to-ASGI wrapper and legacy mount adapter (`app.mount_wsgi()`) to run old WSGI applications at ASGI speeds.
|
|
64
71
|
* **📖 Auto-Generated OpenAPI Docs**: Interactive **Swagger UI** (`/docs`) and **ReDoc** (`/redoc`) instantly generated from your Pydantic schemas and route metadata.
|
|
65
|
-
* **🔌 Modern Communications**: Out-of-the-box support for **WebSockets** and **Server-Sent Events (SSE)**.
|
|
72
|
+
* **🔌 Modern Communications**: Out-of-the-box support for **WebSockets** (with authentication) and **Server-Sent Events (SSE)**.
|
|
73
|
+
* **🔐 WebSocket Authentication**: `WebSocketTokenAuth` dependency for token-based WebSocket authentication via headers or query parameters.
|
|
74
|
+
* **🗄️ Connection Pooling**: Built-in generic `ConnectionPool` and `DatabasePool` with health checks, retry logic, and automatic connection recycling.
|
|
75
|
+
* **🌐 HTTP/2 Push**: `HTTP2Push` utility for server push with Link headers and auto-push decorators.
|
|
76
|
+
* **⏱️ Advanced Rate Limiting**: Per-IP or per-user rate limiting with optional Redis backend for distributed deployments.
|
|
77
|
+
* **📦 Streaming Request Body**: `stream_body()` method for memory-efficient processing of large uploads without buffering.
|
|
78
|
+
* **🗜️ Optimized GZip Compression**: Default compression level 6 (optimal CPU/ratio trade-off) instead of level 9.
|
|
66
79
|
* **🛠️ Premium CLI Tooling**: Visual route tables, interactive app shell, in-memory benchmarking suite, project scaffolding, and environment system inspection.
|
|
67
80
|
* **🐍 Python 3.8–3.13 Compatible**: Full backward compatibility ensured via `typing_extensions` polyfills for `Annotated`, `get_origin`, `get_args`; and a `contextvars`-aware `asyncio.to_thread` shim.
|
|
68
81
|
|
|
@@ -85,7 +98,7 @@ logging.basicConfig(level=logging.INFO)
|
|
|
85
98
|
logger = logging.getLogger("demo")
|
|
86
99
|
|
|
87
100
|
# Initialize the Hybrid App
|
|
88
|
-
app = Fenrir(title="Fenrir Hybrid Demo", version="2.
|
|
101
|
+
app = Fenrir(title="Fenrir Hybrid Demo", version="2.3.2")
|
|
89
102
|
|
|
90
103
|
# --- 1. FastAPI-Style Validation & DI ---
|
|
91
104
|
class UserRegister(BaseModel):
|
|
@@ -129,12 +142,158 @@ if __name__ == "__main__":
|
|
|
129
142
|
|
|
130
143
|
---
|
|
131
144
|
|
|
145
|
+
## 🔺 Trie-Based Routing
|
|
146
|
+
|
|
147
|
+
Fenrir v2.3.2 uses a trie-based routing index for O(k) route matching, where k is the path depth. This is significantly faster than linear O(n) matching when you have many routes.
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from fenrir import Fenrir
|
|
151
|
+
|
|
152
|
+
app = Fenrir()
|
|
153
|
+
|
|
154
|
+
# These routes are indexed in a trie for fast lookup
|
|
155
|
+
@app.get("/api/v1/users")
|
|
156
|
+
async def list_users(): ...
|
|
157
|
+
|
|
158
|
+
@app.get("/api/v1/users/<int:user_id>")
|
|
159
|
+
async def get_user(user_id: int): ...
|
|
160
|
+
|
|
161
|
+
@app.get("/api/v1/posts/<int:post_id>/comments")
|
|
162
|
+
async def get_comments(post_id: int): ...
|
|
163
|
+
|
|
164
|
+
# Route matching is O(k) where k = number of path segments
|
|
165
|
+
# /api/v1/users/42 → checks: api → v1 → users → 42 (parametric)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 🔐 WebSocket Authentication
|
|
171
|
+
|
|
172
|
+
Authenticate WebSocket connections using tokens from headers or query parameters:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from fenrir import Fenrir, WebSocket, Depends
|
|
176
|
+
from fenrir.security import WebSocketTokenAuth
|
|
177
|
+
|
|
178
|
+
app = Fenrir()
|
|
179
|
+
auth = WebSocketTokenAuth()
|
|
180
|
+
|
|
181
|
+
@app.websocket("/ws")
|
|
182
|
+
async def websocket_handler(websocket: WebSocket, token: str = Depends(auth)):
|
|
183
|
+
await websocket.accept()
|
|
184
|
+
await websocket.send_text(f"Authenticated with token: {token}")
|
|
185
|
+
while True:
|
|
186
|
+
data = await websocket.receive_text()
|
|
187
|
+
await websocket.send_text(f"Echo: {data}")
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 🗄️ Connection Pooling
|
|
193
|
+
|
|
194
|
+
Built-in connection pooling for databases and external services:
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from fenrir import Fenrir
|
|
198
|
+
from fenrir.pool import ConnectionPool
|
|
199
|
+
|
|
200
|
+
app = Fenrir()
|
|
201
|
+
|
|
202
|
+
# Create a connection pool
|
|
203
|
+
pool = ConnectionPool(
|
|
204
|
+
create_func=lambda: create_engine("sqlite:///db.sqlite3"),
|
|
205
|
+
close_func=lambda engine: engine.dispose(),
|
|
206
|
+
min_size=2,
|
|
207
|
+
max_size=10,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
@app.get("/users")
|
|
211
|
+
async def list_users():
|
|
212
|
+
async with pool.acquire() as conn:
|
|
213
|
+
result = conn.execute("SELECT * FROM users")
|
|
214
|
+
return {"users": [dict(row) for row in result]}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 🌐 HTTP/2 Push
|
|
220
|
+
|
|
221
|
+
Proactively push resources to clients before they request them:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from fenrir import Fenrir
|
|
225
|
+
from fenrir.http2 import HTTP2Push
|
|
226
|
+
|
|
227
|
+
app = Fenrir()
|
|
228
|
+
push = HTTP2Push()
|
|
229
|
+
|
|
230
|
+
@app.get("/")
|
|
231
|
+
async def index():
|
|
232
|
+
return push.push(
|
|
233
|
+
"<html><link rel='stylesheet' href='/static/style.css'></html>",
|
|
234
|
+
push_paths=["/static/style.css", "/static/app.js"],
|
|
235
|
+
)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## ⏱️ Advanced Rate Limiting
|
|
241
|
+
|
|
242
|
+
Per-IP or per-user rate limiting with optional Redis backend:
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
from fenrir import Fenrir
|
|
246
|
+
from fenrir.middleware import RateLimitMiddleware
|
|
247
|
+
|
|
248
|
+
app = Fenrir()
|
|
249
|
+
|
|
250
|
+
# Per-IP rate limiting
|
|
251
|
+
app.add_middleware(RateLimitMiddleware, max_requests=100, window_seconds=60)
|
|
252
|
+
|
|
253
|
+
# Per-user rate limiting
|
|
254
|
+
def user_key(scope):
|
|
255
|
+
for k, v in scope.get("headers", []):
|
|
256
|
+
if k == b"x-user-id":
|
|
257
|
+
return v.decode("latin-1")
|
|
258
|
+
client = scope.get("client")
|
|
259
|
+
return client[0] if client else "unknown"
|
|
260
|
+
|
|
261
|
+
app.add_middleware(RateLimitMiddleware, key_func=user_key)
|
|
262
|
+
|
|
263
|
+
# Distributed rate limiting with Redis
|
|
264
|
+
import redis.asyncio as aioredis
|
|
265
|
+
redis_client = aioredis.Redis()
|
|
266
|
+
app.add_middleware(RateLimitMiddleware, redis_client=redis_client)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 📦 Streaming Request Body
|
|
272
|
+
|
|
273
|
+
Process large uploads efficiently without buffering the entire body:
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
from fenrir import Fenrir, Request
|
|
277
|
+
|
|
278
|
+
app = Fenrir()
|
|
279
|
+
|
|
280
|
+
@app.post("/upload")
|
|
281
|
+
async def upload(request: Request):
|
|
282
|
+
total_bytes = 0
|
|
283
|
+
async for chunk in request.stream_body(chunk_size=65536):
|
|
284
|
+
total_bytes += len(chunk)
|
|
285
|
+
# Process each chunk without loading entire body into memory
|
|
286
|
+
return {"bytes_received": total_bytes}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
132
291
|
## 💻 CLI Command Reference
|
|
133
292
|
|
|
134
293
|
Fenrir comes packed with a high-fidelity, visually rich command-line tool. Start the CLI by executing `fenrir` or `python -m fenrir.cli`.
|
|
135
294
|
|
|
136
295
|
### 1. `fenrir run`
|
|
137
|
-
Serve your application locally. Powered by **Asteri
|
|
296
|
+
Serve your application locally. Powered by **Asteri**, supporting dynamic multiprocessing, worker management, and live hot-reloading.
|
|
138
297
|
```bash
|
|
139
298
|
fenrir run demo_app:app --port 8000 --dev
|
|
140
299
|
```
|
|
@@ -180,7 +339,7 @@ fenrir info demo_app:app
|
|
|
180
339
|
|
|
181
340
|
## 🧪 Comprehensive Test Suite
|
|
182
341
|
|
|
183
|
-
Fenrir is thoroughly covered by an automated test suite comprising **
|
|
342
|
+
Fenrir is thoroughly covered by an automated test suite comprising **568 tests** validating every single component, including the new trie-based routing, streaming body, connection pooling, HTTP/2 push, WebSocket authentication, and rate limiting features. The suite runs automatically via **GitHub Actions** on every push across Python **3.8 – 3.13**.
|
|
184
343
|
|
|
185
344
|
Run the test suite locally:
|
|
186
345
|
|
|
@@ -190,13 +349,46 @@ PYTHONPATH=. pytest -v
|
|
|
190
349
|
|
|
191
350
|
### Output:
|
|
192
351
|
```text
|
|
193
|
-
|
|
352
|
+
=============================== 568 passed, 1 skipped in 6.72s ===============================
|
|
194
353
|
```
|
|
195
354
|
|
|
196
355
|
---
|
|
197
356
|
|
|
198
357
|
## 🔄 Changelog
|
|
199
358
|
|
|
359
|
+
### v2.3.2 — Architecture & Performance Upgrade
|
|
360
|
+
|
|
361
|
+
Major architecture improvements, new features, and performance optimizations:
|
|
362
|
+
|
|
363
|
+
**Architecture Improvements**
|
|
364
|
+
- **Trie-Based Routing**: Replaced O(n) linear route matching with O(k) trie-based routing. Route lookup now scales with path depth, not total route count.
|
|
365
|
+
- **Context Vars Migration**: Removed `sys._fenrir_active_app` hack, replaced with proper `contextvars.ContextVar` for thread/async-task-safe app context.
|
|
366
|
+
|
|
367
|
+
**New Components**
|
|
368
|
+
- **Connection Pooling (`fenrir.pool`)**: Generic `ConnectionPool` and `DatabasePool` with health checks, retry logic, automatic connection recycling, and configurable pool sizes.
|
|
369
|
+
- **HTTP/2 Push (`fenrir.http2`)**: `HTTP2Push` utility for server push with Link headers, auto-push decorators, and resource type guessing.
|
|
370
|
+
- **WebSocket Authentication (`fenrir.security`)**: `WebSocketTokenAuth` dependency for token-based WebSocket authentication via headers or query parameters.
|
|
371
|
+
|
|
372
|
+
**New Features**
|
|
373
|
+
- **Streaming Request Body**: `request.stream_body()` method for memory-efficient processing of large uploads without buffering.
|
|
374
|
+
- **Per-User Rate Limiting**: `key_func` parameter in `RateLimitMiddleware` for custom rate limiting keys (user ID, API key, etc.).
|
|
375
|
+
- **Distributed Rate Limiting**: Redis backend support for `RateLimitMiddleware` using sliding window algorithm.
|
|
376
|
+
|
|
377
|
+
**Performance Optimizations**
|
|
378
|
+
- **GZip Compression Level**: Default `compresslevel` changed from 9 to 6 for optimal CPU/ratio trade-off.
|
|
379
|
+
- **Redis Rate Limiter**: Uses `time.monotonic()` instead of `time.time()` for clock-safe operation, with unique IDs to prevent collisions.
|
|
380
|
+
- **Deprecated API Fix**: Replaced deprecated `asyncio.get_event_loop()` with `asyncio.get_running_loop()` in WSGI adapter.
|
|
381
|
+
|
|
382
|
+
**Bug Fixes**
|
|
383
|
+
- Fixed missing `import sys` in `app.py` that silently broke root_path detection.
|
|
384
|
+
- Fixed stale `sys._fenrir_active_app` references in `views.py` and `templating.py`.
|
|
385
|
+
- Fixed inconsistent version strings across `pyproject.toml`, `__init__.py`, and `app.py`.
|
|
386
|
+
- Fixed unused `import asyncio` in `falcon.py`.
|
|
387
|
+
- Removed private `Semaphore._value` access from `Pool.stats`.
|
|
388
|
+
|
|
389
|
+
**New Exports**
|
|
390
|
+
- `RouteTrie`, `WebSocketTokenAuth`, `ConnectionPool`, `DatabasePool`, `HTTP2Push`
|
|
391
|
+
|
|
200
392
|
### v2.2.2 — Major Feature Update
|
|
201
393
|
|
|
202
394
|
New middleware, session backends, pagination, and more:
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
[](https://pypi.org/project/fenrir-framework/)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
9
|
[](https://www.python.org/)
|
|
10
|
-
[](https://github.com/IshikawaUta/fenrir/actions)
|
|
11
11
|
[](https://github.com/IshikawaUta/fenrir/actions/workflows/test.yml)
|
|
12
12
|
[]()
|
|
13
13
|
|
|
14
|
-
**Fenrir** is a state-of-the-art, high-performance, hybrid Python web framework built on top of modern ASGI specifications. It elegantly merges the best programming paradigms from Python's most popular web frameworks (**Flask**, **FastAPI**, **Sanic**, **Falcon**, and **Bottle**) into a single unified workspace, powered locally by the premium **Asteri
|
|
14
|
+
**Fenrir** is a state-of-the-art, high-performance, hybrid Python web framework built on top of modern ASGI specifications. It elegantly merges the best programming paradigms from Python's most popular web frameworks (**Flask**, **FastAPI**, **Sanic**, **Falcon**, and **Bottle**) into a single unified workspace, powered locally by the premium **Asteri** application server.
|
|
15
15
|
|
|
16
16
|
Whether you prefer the automatic Pydantic validation of FastAPI, the seamless context-locals of Flask, the raw class-based speed of Falcon, or the robust background task model of Sanic, **Fenrir** allows you to leverage them all simultaneously in the same codebase.
|
|
17
17
|
|
|
@@ -25,6 +25,12 @@ Install directly from **PyPI**:
|
|
|
25
25
|
pip install fenrir-framework
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
Or install with Redis support for distributed sessions and rate limiting:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install fenrir-framework[redis]
|
|
32
|
+
```
|
|
33
|
+
|
|
28
34
|
Or install in development mode by cloning the repository:
|
|
29
35
|
|
|
30
36
|
```bash
|
|
@@ -38,6 +44,7 @@ pip install -e .
|
|
|
38
44
|
## 🌟 Key Features
|
|
39
45
|
|
|
40
46
|
* **⚡ High-Speed ASGI Core**: Extremely low-overhead routing and handler pipeline, achieving massive request throughput.
|
|
47
|
+
* **🔺 Trie-Based Routing**: O(k) route matching where k = path depth, instead of O(n) linear scan. Handles 1000+ routes efficiently.
|
|
41
48
|
* **🧩 Framework Hybridization**:
|
|
42
49
|
* **FastAPI Paradigm**: Native Pydantic v2 data validation, `Annotated` type decorators, automated parameter resolution (`Query`, `Path`, `Header`, `Cookie`, `Body`), dynamic dependency injection (`Depends`), and automated `response_model` serialization.
|
|
43
50
|
* **Flask Paradigm**: Thread/Task-safe context locals (`request`, `g`, `session`), Jinja2 template rendering (`render_template`), and request teardown hooks.
|
|
@@ -45,7 +52,13 @@ pip install -e .
|
|
|
45
52
|
* **Sanic Paradigm**: Global `sys.modules` patching (`install_sanic_compat()`), standard response helpers (`json`, `text`, `html`, `raw`, `redirect`), lifecycle listeners (`before_server_start`, etc.), and a background event scheduler (`app.add_task`).
|
|
46
53
|
* **Bottle Paradigm**: Built-in WSGI-to-ASGI wrapper and legacy mount adapter (`app.mount_wsgi()`) to run old WSGI applications at ASGI speeds.
|
|
47
54
|
* **📖 Auto-Generated OpenAPI Docs**: Interactive **Swagger UI** (`/docs`) and **ReDoc** (`/redoc`) instantly generated from your Pydantic schemas and route metadata.
|
|
48
|
-
* **🔌 Modern Communications**: Out-of-the-box support for **WebSockets** and **Server-Sent Events (SSE)**.
|
|
55
|
+
* **🔌 Modern Communications**: Out-of-the-box support for **WebSockets** (with authentication) and **Server-Sent Events (SSE)**.
|
|
56
|
+
* **🔐 WebSocket Authentication**: `WebSocketTokenAuth` dependency for token-based WebSocket authentication via headers or query parameters.
|
|
57
|
+
* **🗄️ Connection Pooling**: Built-in generic `ConnectionPool` and `DatabasePool` with health checks, retry logic, and automatic connection recycling.
|
|
58
|
+
* **🌐 HTTP/2 Push**: `HTTP2Push` utility for server push with Link headers and auto-push decorators.
|
|
59
|
+
* **⏱️ Advanced Rate Limiting**: Per-IP or per-user rate limiting with optional Redis backend for distributed deployments.
|
|
60
|
+
* **📦 Streaming Request Body**: `stream_body()` method for memory-efficient processing of large uploads without buffering.
|
|
61
|
+
* **🗜️ Optimized GZip Compression**: Default compression level 6 (optimal CPU/ratio trade-off) instead of level 9.
|
|
49
62
|
* **🛠️ Premium CLI Tooling**: Visual route tables, interactive app shell, in-memory benchmarking suite, project scaffolding, and environment system inspection.
|
|
50
63
|
* **🐍 Python 3.8–3.13 Compatible**: Full backward compatibility ensured via `typing_extensions` polyfills for `Annotated`, `get_origin`, `get_args`; and a `contextvars`-aware `asyncio.to_thread` shim.
|
|
51
64
|
|
|
@@ -68,7 +81,7 @@ logging.basicConfig(level=logging.INFO)
|
|
|
68
81
|
logger = logging.getLogger("demo")
|
|
69
82
|
|
|
70
83
|
# Initialize the Hybrid App
|
|
71
|
-
app = Fenrir(title="Fenrir Hybrid Demo", version="2.
|
|
84
|
+
app = Fenrir(title="Fenrir Hybrid Demo", version="2.3.2")
|
|
72
85
|
|
|
73
86
|
# --- 1. FastAPI-Style Validation & DI ---
|
|
74
87
|
class UserRegister(BaseModel):
|
|
@@ -112,12 +125,158 @@ if __name__ == "__main__":
|
|
|
112
125
|
|
|
113
126
|
---
|
|
114
127
|
|
|
128
|
+
## 🔺 Trie-Based Routing
|
|
129
|
+
|
|
130
|
+
Fenrir v2.3.2 uses a trie-based routing index for O(k) route matching, where k is the path depth. This is significantly faster than linear O(n) matching when you have many routes.
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from fenrir import Fenrir
|
|
134
|
+
|
|
135
|
+
app = Fenrir()
|
|
136
|
+
|
|
137
|
+
# These routes are indexed in a trie for fast lookup
|
|
138
|
+
@app.get("/api/v1/users")
|
|
139
|
+
async def list_users(): ...
|
|
140
|
+
|
|
141
|
+
@app.get("/api/v1/users/<int:user_id>")
|
|
142
|
+
async def get_user(user_id: int): ...
|
|
143
|
+
|
|
144
|
+
@app.get("/api/v1/posts/<int:post_id>/comments")
|
|
145
|
+
async def get_comments(post_id: int): ...
|
|
146
|
+
|
|
147
|
+
# Route matching is O(k) where k = number of path segments
|
|
148
|
+
# /api/v1/users/42 → checks: api → v1 → users → 42 (parametric)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 🔐 WebSocket Authentication
|
|
154
|
+
|
|
155
|
+
Authenticate WebSocket connections using tokens from headers or query parameters:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from fenrir import Fenrir, WebSocket, Depends
|
|
159
|
+
from fenrir.security import WebSocketTokenAuth
|
|
160
|
+
|
|
161
|
+
app = Fenrir()
|
|
162
|
+
auth = WebSocketTokenAuth()
|
|
163
|
+
|
|
164
|
+
@app.websocket("/ws")
|
|
165
|
+
async def websocket_handler(websocket: WebSocket, token: str = Depends(auth)):
|
|
166
|
+
await websocket.accept()
|
|
167
|
+
await websocket.send_text(f"Authenticated with token: {token}")
|
|
168
|
+
while True:
|
|
169
|
+
data = await websocket.receive_text()
|
|
170
|
+
await websocket.send_text(f"Echo: {data}")
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 🗄️ Connection Pooling
|
|
176
|
+
|
|
177
|
+
Built-in connection pooling for databases and external services:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from fenrir import Fenrir
|
|
181
|
+
from fenrir.pool import ConnectionPool
|
|
182
|
+
|
|
183
|
+
app = Fenrir()
|
|
184
|
+
|
|
185
|
+
# Create a connection pool
|
|
186
|
+
pool = ConnectionPool(
|
|
187
|
+
create_func=lambda: create_engine("sqlite:///db.sqlite3"),
|
|
188
|
+
close_func=lambda engine: engine.dispose(),
|
|
189
|
+
min_size=2,
|
|
190
|
+
max_size=10,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
@app.get("/users")
|
|
194
|
+
async def list_users():
|
|
195
|
+
async with pool.acquire() as conn:
|
|
196
|
+
result = conn.execute("SELECT * FROM users")
|
|
197
|
+
return {"users": [dict(row) for row in result]}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## 🌐 HTTP/2 Push
|
|
203
|
+
|
|
204
|
+
Proactively push resources to clients before they request them:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from fenrir import Fenrir
|
|
208
|
+
from fenrir.http2 import HTTP2Push
|
|
209
|
+
|
|
210
|
+
app = Fenrir()
|
|
211
|
+
push = HTTP2Push()
|
|
212
|
+
|
|
213
|
+
@app.get("/")
|
|
214
|
+
async def index():
|
|
215
|
+
return push.push(
|
|
216
|
+
"<html><link rel='stylesheet' href='/static/style.css'></html>",
|
|
217
|
+
push_paths=["/static/style.css", "/static/app.js"],
|
|
218
|
+
)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## ⏱️ Advanced Rate Limiting
|
|
224
|
+
|
|
225
|
+
Per-IP or per-user rate limiting with optional Redis backend:
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from fenrir import Fenrir
|
|
229
|
+
from fenrir.middleware import RateLimitMiddleware
|
|
230
|
+
|
|
231
|
+
app = Fenrir()
|
|
232
|
+
|
|
233
|
+
# Per-IP rate limiting
|
|
234
|
+
app.add_middleware(RateLimitMiddleware, max_requests=100, window_seconds=60)
|
|
235
|
+
|
|
236
|
+
# Per-user rate limiting
|
|
237
|
+
def user_key(scope):
|
|
238
|
+
for k, v in scope.get("headers", []):
|
|
239
|
+
if k == b"x-user-id":
|
|
240
|
+
return v.decode("latin-1")
|
|
241
|
+
client = scope.get("client")
|
|
242
|
+
return client[0] if client else "unknown"
|
|
243
|
+
|
|
244
|
+
app.add_middleware(RateLimitMiddleware, key_func=user_key)
|
|
245
|
+
|
|
246
|
+
# Distributed rate limiting with Redis
|
|
247
|
+
import redis.asyncio as aioredis
|
|
248
|
+
redis_client = aioredis.Redis()
|
|
249
|
+
app.add_middleware(RateLimitMiddleware, redis_client=redis_client)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 📦 Streaming Request Body
|
|
255
|
+
|
|
256
|
+
Process large uploads efficiently without buffering the entire body:
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
from fenrir import Fenrir, Request
|
|
260
|
+
|
|
261
|
+
app = Fenrir()
|
|
262
|
+
|
|
263
|
+
@app.post("/upload")
|
|
264
|
+
async def upload(request: Request):
|
|
265
|
+
total_bytes = 0
|
|
266
|
+
async for chunk in request.stream_body(chunk_size=65536):
|
|
267
|
+
total_bytes += len(chunk)
|
|
268
|
+
# Process each chunk without loading entire body into memory
|
|
269
|
+
return {"bytes_received": total_bytes}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
115
274
|
## 💻 CLI Command Reference
|
|
116
275
|
|
|
117
276
|
Fenrir comes packed with a high-fidelity, visually rich command-line tool. Start the CLI by executing `fenrir` or `python -m fenrir.cli`.
|
|
118
277
|
|
|
119
278
|
### 1. `fenrir run`
|
|
120
|
-
Serve your application locally. Powered by **Asteri
|
|
279
|
+
Serve your application locally. Powered by **Asteri**, supporting dynamic multiprocessing, worker management, and live hot-reloading.
|
|
121
280
|
```bash
|
|
122
281
|
fenrir run demo_app:app --port 8000 --dev
|
|
123
282
|
```
|
|
@@ -163,7 +322,7 @@ fenrir info demo_app:app
|
|
|
163
322
|
|
|
164
323
|
## 🧪 Comprehensive Test Suite
|
|
165
324
|
|
|
166
|
-
Fenrir is thoroughly covered by an automated test suite comprising **
|
|
325
|
+
Fenrir is thoroughly covered by an automated test suite comprising **568 tests** validating every single component, including the new trie-based routing, streaming body, connection pooling, HTTP/2 push, WebSocket authentication, and rate limiting features. The suite runs automatically via **GitHub Actions** on every push across Python **3.8 – 3.13**.
|
|
167
326
|
|
|
168
327
|
Run the test suite locally:
|
|
169
328
|
|
|
@@ -173,13 +332,46 @@ PYTHONPATH=. pytest -v
|
|
|
173
332
|
|
|
174
333
|
### Output:
|
|
175
334
|
```text
|
|
176
|
-
|
|
335
|
+
=============================== 568 passed, 1 skipped in 6.72s ===============================
|
|
177
336
|
```
|
|
178
337
|
|
|
179
338
|
---
|
|
180
339
|
|
|
181
340
|
## 🔄 Changelog
|
|
182
341
|
|
|
342
|
+
### v2.3.2 — Architecture & Performance Upgrade
|
|
343
|
+
|
|
344
|
+
Major architecture improvements, new features, and performance optimizations:
|
|
345
|
+
|
|
346
|
+
**Architecture Improvements**
|
|
347
|
+
- **Trie-Based Routing**: Replaced O(n) linear route matching with O(k) trie-based routing. Route lookup now scales with path depth, not total route count.
|
|
348
|
+
- **Context Vars Migration**: Removed `sys._fenrir_active_app` hack, replaced with proper `contextvars.ContextVar` for thread/async-task-safe app context.
|
|
349
|
+
|
|
350
|
+
**New Components**
|
|
351
|
+
- **Connection Pooling (`fenrir.pool`)**: Generic `ConnectionPool` and `DatabasePool` with health checks, retry logic, automatic connection recycling, and configurable pool sizes.
|
|
352
|
+
- **HTTP/2 Push (`fenrir.http2`)**: `HTTP2Push` utility for server push with Link headers, auto-push decorators, and resource type guessing.
|
|
353
|
+
- **WebSocket Authentication (`fenrir.security`)**: `WebSocketTokenAuth` dependency for token-based WebSocket authentication via headers or query parameters.
|
|
354
|
+
|
|
355
|
+
**New Features**
|
|
356
|
+
- **Streaming Request Body**: `request.stream_body()` method for memory-efficient processing of large uploads without buffering.
|
|
357
|
+
- **Per-User Rate Limiting**: `key_func` parameter in `RateLimitMiddleware` for custom rate limiting keys (user ID, API key, etc.).
|
|
358
|
+
- **Distributed Rate Limiting**: Redis backend support for `RateLimitMiddleware` using sliding window algorithm.
|
|
359
|
+
|
|
360
|
+
**Performance Optimizations**
|
|
361
|
+
- **GZip Compression Level**: Default `compresslevel` changed from 9 to 6 for optimal CPU/ratio trade-off.
|
|
362
|
+
- **Redis Rate Limiter**: Uses `time.monotonic()` instead of `time.time()` for clock-safe operation, with unique IDs to prevent collisions.
|
|
363
|
+
- **Deprecated API Fix**: Replaced deprecated `asyncio.get_event_loop()` with `asyncio.get_running_loop()` in WSGI adapter.
|
|
364
|
+
|
|
365
|
+
**Bug Fixes**
|
|
366
|
+
- Fixed missing `import sys` in `app.py` that silently broke root_path detection.
|
|
367
|
+
- Fixed stale `sys._fenrir_active_app` references in `views.py` and `templating.py`.
|
|
368
|
+
- Fixed inconsistent version strings across `pyproject.toml`, `__init__.py`, and `app.py`.
|
|
369
|
+
- Fixed unused `import asyncio` in `falcon.py`.
|
|
370
|
+
- Removed private `Semaphore._value` access from `Pool.stats`.
|
|
371
|
+
|
|
372
|
+
**New Exports**
|
|
373
|
+
- `RouteTrie`, `WebSocketTokenAuth`, `ConnectionPool`, `DatabasePool`, `HTTP2Push`
|
|
374
|
+
|
|
183
375
|
### v2.2.2 — Major Feature Update
|
|
184
376
|
|
|
185
377
|
New middleware, session backends, pagination, and more:
|
|
@@ -27,7 +27,7 @@ from fenrir.response import (
|
|
|
27
27
|
from fenrir.templating import render_template, BaseTemplateRenderer, Jinja2Renderer
|
|
28
28
|
from fenrir.views import View, MethodView
|
|
29
29
|
from fenrir.helpers import url_for, send_file, send_from_directory, redirect
|
|
30
|
-
from fenrir.routing import Router, Route, APIRouter
|
|
30
|
+
from fenrir.routing import Router, Route, APIRouter, RouteTrie
|
|
31
31
|
from fenrir.sse import EventSourceResponse
|
|
32
32
|
from fenrir.security import (
|
|
33
33
|
APIKeyCookie,
|
|
@@ -39,6 +39,7 @@ from fenrir.security import (
|
|
|
39
39
|
OAuth2PasswordBearer,
|
|
40
40
|
OAuth2AuthorizationCodeBearer,
|
|
41
41
|
OpenIDConnect,
|
|
42
|
+
WebSocketTokenAuth,
|
|
42
43
|
)
|
|
43
44
|
from fenrir.background import BackgroundTasks, BackgroundTask
|
|
44
45
|
from fenrir.compat import WsgiToAsgi, install_bottle_compat, install_falcon_compat, install_sanic_compat
|
|
@@ -53,6 +54,8 @@ from fenrir.middleware import (
|
|
|
53
54
|
RequestIDMiddleware,
|
|
54
55
|
RateLimitMiddleware,
|
|
55
56
|
)
|
|
57
|
+
from fenrir.pool import ConnectionPool, DatabasePool
|
|
58
|
+
from fenrir.http2 import HTTP2Push
|
|
56
59
|
from fenrir.pagination import PaginationParams, paginate, paginate_dict
|
|
57
60
|
from fenrir.sessions import (
|
|
58
61
|
RedisSessionInterface,
|
|
@@ -65,7 +68,7 @@ from fenrir.sessions import (
|
|
|
65
68
|
# Re-export Annotated for convenient use with param markers
|
|
66
69
|
from fenrir.compat import Annotated
|
|
67
70
|
|
|
68
|
-
__version__ = "2.
|
|
71
|
+
__version__ = "2.3.3"
|
|
69
72
|
__all__ = [
|
|
70
73
|
# Core app
|
|
71
74
|
"Fenrir",
|
|
@@ -126,6 +129,7 @@ __all__ = [
|
|
|
126
129
|
"Router",
|
|
127
130
|
"Route",
|
|
128
131
|
"APIRouter",
|
|
132
|
+
"RouteTrie",
|
|
129
133
|
# SSE
|
|
130
134
|
"EventSourceResponse",
|
|
131
135
|
# Security
|
|
@@ -138,6 +142,7 @@ __all__ = [
|
|
|
138
142
|
"OAuth2PasswordBearer",
|
|
139
143
|
"OAuth2AuthorizationCodeBearer",
|
|
140
144
|
"OpenIDConnect",
|
|
145
|
+
"WebSocketTokenAuth",
|
|
141
146
|
# Background tasks
|
|
142
147
|
"BackgroundTasks",
|
|
143
148
|
"BackgroundTask",
|
|
@@ -165,6 +170,11 @@ __all__ = [
|
|
|
165
170
|
"PaginationParams",
|
|
166
171
|
"paginate",
|
|
167
172
|
"paginate_dict",
|
|
173
|
+
# Connection Pooling
|
|
174
|
+
"ConnectionPool",
|
|
175
|
+
"DatabasePool",
|
|
176
|
+
# HTTP/2 Push
|
|
177
|
+
"HTTP2Push",
|
|
168
178
|
# Server-side sessions
|
|
169
179
|
"RedisSessionInterface",
|
|
170
180
|
"InMemorySessionInterface",
|