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.

Files changed (88) hide show
  1. {fenrir_framework-2.2.2/fenrir_framework.egg-info → fenrir_framework-2.3.3}/PKG-INFO +200 -8
  2. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/README.md +199 -7
  3. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/__init__.py +12 -2
  4. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/app.py +40 -22
  5. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/cli.py +15 -28
  6. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/compat.py +1 -1
  7. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/context.py +0 -4
  8. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/falcon.py +0 -1
  9. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/helpers.py +1 -2
  10. fenrir_framework-2.3.3/fenrir/http2.py +136 -0
  11. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/middleware.py +54 -8
  12. fenrir_framework-2.3.3/fenrir/pool.py +269 -0
  13. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/request.py +29 -7
  14. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/routing.py +99 -2
  15. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/security.py +71 -0
  16. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/templating.py +1 -3
  17. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/views.py +2 -2
  18. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3/fenrir_framework.egg-info}/PKG-INFO +200 -8
  19. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir_framework.egg-info/SOURCES.txt +3 -0
  20. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/pyproject.toml +1 -1
  21. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_cli.py +3 -7
  22. fenrir_framework-2.3.3/tests/test_v3_features.py +595 -0
  23. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/LICENSE +0 -0
  24. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/background.py +0 -0
  25. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/bottle.py +0 -0
  26. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/config.py +0 -0
  27. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/dependencies.py +0 -0
  28. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/exceptions.py +0 -0
  29. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/json.py +0 -0
  30. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/logo.jpg +0 -0
  31. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/logo.png +0 -0
  32. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/openapi.py +0 -0
  33. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/pagination.py +0 -0
  34. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/response.py +0 -0
  35. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/sanic.py +0 -0
  36. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/sessions.py +0 -0
  37. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/signals.py +0 -0
  38. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/sse.py +0 -0
  39. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/testing.py +0 -0
  40. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/upload.py +0 -0
  41. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir/websocket.py +0 -0
  42. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir_framework.egg-info/dependency_links.txt +0 -0
  43. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir_framework.egg-info/entry_points.txt +0 -0
  44. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir_framework.egg-info/requires.txt +0 -0
  45. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/fenrir_framework.egg-info/top_level.txt +0 -0
  46. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/setup.cfg +0 -0
  47. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_appctx.py +0 -0
  48. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_async.py +0 -0
  49. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_basic.py +0 -0
  50. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_blueprints.py +0 -0
  51. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_circular_deps.py +0 -0
  52. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_config.py +0 -0
  53. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_context.py +0 -0
  54. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_converters.py +0 -0
  55. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_custom_template.py +0 -0
  56. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_dep_overrides.py +0 -0
  57. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_dependencies.py +0 -0
  58. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_falcon_compat.py +0 -0
  59. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_form_file.py +0 -0
  60. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_helpers.py +0 -0
  61. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_instance_config.py +0 -0
  62. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_json.py +0 -0
  63. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_json_tag.py +0 -0
  64. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_logging.py +0 -0
  65. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_logo_route.py +0 -0
  66. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_middleware.py +0 -0
  67. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_new_features.py +0 -0
  68. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_new_middleware_features.py +0 -0
  69. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_regression.py +0 -0
  70. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_reqctx.py +0 -0
  71. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_request.py +0 -0
  72. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_resources.py +0 -0
  73. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_router_circular.py +0 -0
  74. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_routing.py +0 -0
  75. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_sanic_compat.py +0 -0
  76. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_security.py +0 -0
  77. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_session_interface.py +0 -0
  78. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_signals.py +0 -0
  79. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_sse.py +0 -0
  80. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_strict_content_type.py +0 -0
  81. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_subclassing.py +0 -0
  82. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_templating.py +0 -0
  83. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_testing.py +0 -0
  84. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_user_error_handler.py +0 -0
  85. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_validation.py +0 -0
  86. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_views.py +0 -0
  87. {fenrir_framework-2.2.2 → fenrir_framework-2.3.3}/tests/test_websocket.py +0 -0
  88. {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.2.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
  [![PyPI version](https://img.shields.io/pypi/v/fenrir-framework.svg?color=blueviolet)](https://pypi.org/project/fenrir-framework/)
25
25
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
26
26
  [![Python Version](https://img.shields.io/badge/Python-3.8%2B-blue.svg)](https://www.python.org/)
27
- [![Tests](https://img.shields.io/badge/Tests-528%20Passed-brightgreen.svg)](https://github.com/IshikawaUta/fenrir/actions)
27
+ [![Tests](https://img.shields.io/badge/Tests-568%20Passed-brightgreen.svg)](https://github.com/IshikawaUta/fenrir/actions)
28
28
  [![CI](https://github.com/IshikawaUta/fenrir/actions/workflows/test.yml/badge.svg)](https://github.com/IshikawaUta/fenrir/actions/workflows/test.yml)
29
29
  [![Performance](https://img.shields.io/badge/Performance-High--Speed%20ASGI-orange.svg)]()
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 v2.2.2** application server.
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.2.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 v2.2.2**, supporting dynamic multiprocessing, worker management, and live hot-reloading.
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 **528 tests** validating every single component, compat namespace, file upload, routing detail, and CLI functionality. The suite runs automatically via **GitHub Actions** on every push across Python **3.8 – 3.13**.
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
- ======================= 528 passed, 1 skipped in 3.4s ========================
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
  [![PyPI version](https://img.shields.io/pypi/v/fenrir-framework.svg?color=blueviolet)](https://pypi.org/project/fenrir-framework/)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
9
9
  [![Python Version](https://img.shields.io/badge/Python-3.8%2B-blue.svg)](https://www.python.org/)
10
- [![Tests](https://img.shields.io/badge/Tests-528%20Passed-brightgreen.svg)](https://github.com/IshikawaUta/fenrir/actions)
10
+ [![Tests](https://img.shields.io/badge/Tests-568%20Passed-brightgreen.svg)](https://github.com/IshikawaUta/fenrir/actions)
11
11
  [![CI](https://github.com/IshikawaUta/fenrir/actions/workflows/test.yml/badge.svg)](https://github.com/IshikawaUta/fenrir/actions/workflows/test.yml)
12
12
  [![Performance](https://img.shields.io/badge/Performance-High--Speed%20ASGI-orange.svg)]()
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 v2.2.2** application server.
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.2.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 v2.2.2**, supporting dynamic multiprocessing, worker management, and live hot-reloading.
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 **528 tests** validating every single component, compat namespace, file upload, routing detail, and CLI functionality. The suite runs automatically via **GitHub Actions** on every push across Python **3.8 – 3.13**.
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
- ======================= 528 passed, 1 skipped in 3.4s ========================
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.2.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",