fenrir-framework 0.1.0__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.
- fenrir_framework-0.1.0/LICENSE +21 -0
- fenrir_framework-0.1.0/PKG-INFO +217 -0
- fenrir_framework-0.1.0/README.md +203 -0
- fenrir_framework-0.1.0/fenrir/__init__.py +148 -0
- fenrir_framework-0.1.0/fenrir/app.py +807 -0
- fenrir_framework-0.1.0/fenrir/background.py +70 -0
- fenrir_framework-0.1.0/fenrir/bottle.py +4583 -0
- fenrir_framework-0.1.0/fenrir/cli.py +860 -0
- fenrir_framework-0.1.0/fenrir/compat.py +180 -0
- fenrir_framework-0.1.0/fenrir/config.py +46 -0
- fenrir_framework-0.1.0/fenrir/context.py +126 -0
- fenrir_framework-0.1.0/fenrir/dependencies.py +407 -0
- fenrir_framework-0.1.0/fenrir/exceptions.py +46 -0
- fenrir_framework-0.1.0/fenrir/falcon.py +99 -0
- fenrir_framework-0.1.0/fenrir/helpers.py +139 -0
- fenrir_framework-0.1.0/fenrir/json.py +139 -0
- fenrir_framework-0.1.0/fenrir/openapi.py +294 -0
- fenrir_framework-0.1.0/fenrir/request.py +189 -0
- fenrir_framework-0.1.0/fenrir/response.py +332 -0
- fenrir_framework-0.1.0/fenrir/routing.py +331 -0
- fenrir_framework-0.1.0/fenrir/sanic.py +50 -0
- fenrir_framework-0.1.0/fenrir/security.py +326 -0
- fenrir_framework-0.1.0/fenrir/sessions.py +93 -0
- fenrir_framework-0.1.0/fenrir/signals.py +55 -0
- fenrir_framework-0.1.0/fenrir/sse.py +75 -0
- fenrir_framework-0.1.0/fenrir/templating.py +55 -0
- fenrir_framework-0.1.0/fenrir/testing.py +43 -0
- fenrir_framework-0.1.0/fenrir/upload.py +32 -0
- fenrir_framework-0.1.0/fenrir/views.py +92 -0
- fenrir_framework-0.1.0/fenrir/websocket.py +72 -0
- fenrir_framework-0.1.0/fenrir_framework.egg-info/PKG-INFO +217 -0
- fenrir_framework-0.1.0/fenrir_framework.egg-info/SOURCES.txt +78 -0
- fenrir_framework-0.1.0/fenrir_framework.egg-info/dependency_links.txt +1 -0
- fenrir_framework-0.1.0/fenrir_framework.egg-info/entry_points.txt +2 -0
- fenrir_framework-0.1.0/fenrir_framework.egg-info/requires.txt +5 -0
- fenrir_framework-0.1.0/fenrir_framework.egg-info/top_level.txt +1 -0
- fenrir_framework-0.1.0/pyproject.toml +23 -0
- fenrir_framework-0.1.0/setup.cfg +4 -0
- fenrir_framework-0.1.0/tests/test_appctx.py +24 -0
- fenrir_framework-0.1.0/tests/test_async.py +21 -0
- fenrir_framework-0.1.0/tests/test_basic.py +26 -0
- fenrir_framework-0.1.0/tests/test_blueprints.py +34 -0
- fenrir_framework-0.1.0/tests/test_circular_deps.py +36 -0
- fenrir_framework-0.1.0/tests/test_cli.py +125 -0
- fenrir_framework-0.1.0/tests/test_config.py +35 -0
- fenrir_framework-0.1.0/tests/test_context.py +36 -0
- fenrir_framework-0.1.0/tests/test_converters.py +31 -0
- fenrir_framework-0.1.0/tests/test_custom_template.py +24 -0
- fenrir_framework-0.1.0/tests/test_dep_overrides.py +38 -0
- fenrir_framework-0.1.0/tests/test_dependencies.py +24 -0
- fenrir_framework-0.1.0/tests/test_falcon_compat.py +130 -0
- fenrir_framework-0.1.0/tests/test_form_file.py +47 -0
- fenrir_framework-0.1.0/tests/test_helpers.py +65 -0
- fenrir_framework-0.1.0/tests/test_instance_config.py +17 -0
- fenrir_framework-0.1.0/tests/test_json.py +33 -0
- fenrir_framework-0.1.0/tests/test_json_tag.py +30 -0
- fenrir_framework-0.1.0/tests/test_logging.py +11 -0
- fenrir_framework-0.1.0/tests/test_logo_route.py +45 -0
- fenrir_framework-0.1.0/tests/test_middleware.py +87 -0
- fenrir_framework-0.1.0/tests/test_new_features.py +391 -0
- fenrir_framework-0.1.0/tests/test_regression.py +13 -0
- fenrir_framework-0.1.0/tests/test_reqctx.py +9 -0
- fenrir_framework-0.1.0/tests/test_request.py +24 -0
- fenrir_framework-0.1.0/tests/test_resources.py +33 -0
- fenrir_framework-0.1.0/tests/test_router_circular.py +40 -0
- fenrir_framework-0.1.0/tests/test_routing.py +72 -0
- fenrir_framework-0.1.0/tests/test_sanic_compat.py +178 -0
- fenrir_framework-0.1.0/tests/test_security.py +128 -0
- fenrir_framework-0.1.0/tests/test_session_interface.py +27 -0
- fenrir_framework-0.1.0/tests/test_signals.py +53 -0
- fenrir_framework-0.1.0/tests/test_sse.py +54 -0
- fenrir_framework-0.1.0/tests/test_strict_content_type.py +51 -0
- fenrir_framework-0.1.0/tests/test_subclassing.py +14 -0
- fenrir_framework-0.1.0/tests/test_templating.py +18 -0
- fenrir_framework-0.1.0/tests/test_testing.py +12 -0
- fenrir_framework-0.1.0/tests/test_user_error_handler.py +37 -0
- fenrir_framework-0.1.0/tests/test_validation.py +54 -0
- fenrir_framework-0.1.0/tests/test_views.py +27 -0
- fenrir_framework-0.1.0/tests/test_websocket.py +140 -0
- fenrir_framework-0.1.0/tests/test_yield_deps.py +96 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 IshikawaUta
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fenrir-framework
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A hybrid Python web framework combining Flask, FastAPI, Bottle, Falcon, and Sanic
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: pydantic>=2.0.0
|
|
9
|
+
Requires-Dist: jinja2>=3.0.0
|
|
10
|
+
Requires-Dist: asteri>=2.2.2
|
|
11
|
+
Requires-Dist: itsdangerous>=2.0.0
|
|
12
|
+
Requires-Dist: python-multipart>=0.0.18
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img src="logo.jpg" alt="Fenrir Logo" width="300px"/>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
# Fenrir Web Framework
|
|
20
|
+
|
|
21
|
+
[](https://opensource.org/licenses/MIT)
|
|
22
|
+
[](https://www.python.org/)
|
|
23
|
+
[]()
|
|
24
|
+
[]()
|
|
25
|
+
|
|
26
|
+
**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.
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🌟 Key Features
|
|
33
|
+
|
|
34
|
+
* **⚡ High-Speed ASGI Core**: Extremely low-overhead routing and handler pipeline, achieving massive request throughput.
|
|
35
|
+
* **🧩 Framework Hybridization**:
|
|
36
|
+
* **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.
|
|
37
|
+
* **Flask Paradigm**: Thread/Task-safe context locals (`request`, `g`, `session`), Jinja2 template rendering (`render_template`), and request teardown hooks.
|
|
38
|
+
* **Falcon Paradigm**: Class-based resource controllers (`on_get`, `on_post`), before/after hooks, and in-place response mutation.
|
|
39
|
+
* **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`).
|
|
40
|
+
* **Bottle Paradigm**: Built-in WSGI-to-ASGI wrapper and legacy mount adapter (`app.mount_wsgi()`) to run old WSGI applications at ASGI speeds.
|
|
41
|
+
* **📖 Auto-Generated OpenAPI Docs**: Interactive **Swagger UI** (`/docs`) and **ReDoc** (`/redoc`) instantly generated from your Pydantic schemas and route metadata.
|
|
42
|
+
* **🔌 Modern Communications**: Out-of-the-box support for **WebSockets** and **Server-Sent Events (SSE)**.
|
|
43
|
+
* **🛠️ Premium CLI Tooling**: Visual route tables, interactive app shell, in-memory benchmarking suite, project scaffolding, and environment system inspection.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 📦 Installation
|
|
48
|
+
|
|
49
|
+
To install Fenrir in development mode, clone the repository and run:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install -e .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
To install all optimal development and testing dependencies:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install pytest httpx watchdog pydantic jinja2 asteri>=2.2.2
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 🚀 Quick Start (The Hybrid Power)
|
|
64
|
+
|
|
65
|
+
Here is a simple example (`demo_app.py`) showcasing how Flask, FastAPI, Falcon, and Sanic styles coexist harmoniously in a single application:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import logging
|
|
69
|
+
from pydantic import BaseModel
|
|
70
|
+
from fenrir import (
|
|
71
|
+
Fenrir, Blueprint, request, g, Depends, Query, Header,
|
|
72
|
+
render_template, Response, Form, File, UploadFile,
|
|
73
|
+
WebSocket, WebSocketDisconnect
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
logging.basicConfig(level=logging.INFO)
|
|
77
|
+
logger = logging.getLogger("demo")
|
|
78
|
+
|
|
79
|
+
# Initialize the Hybrid App
|
|
80
|
+
app = Fenrir(title="Fenrir Hybrid Demo", version="1.0.0")
|
|
81
|
+
|
|
82
|
+
# --- 1. FastAPI-Style Validation & DI ---
|
|
83
|
+
class UserRegister(BaseModel):
|
|
84
|
+
username: str
|
|
85
|
+
email: str
|
|
86
|
+
age: int
|
|
87
|
+
|
|
88
|
+
async def verify_api_key(x_api_key: str = Header(default=None)):
|
|
89
|
+
if x_api_key != "super-secret-key":
|
|
90
|
+
logger.warning("Invalid API key attempt")
|
|
91
|
+
return x_api_key
|
|
92
|
+
|
|
93
|
+
# --- 2. Flask-Style Routing & Context-Locals ---
|
|
94
|
+
@app.get("/")
|
|
95
|
+
async def home():
|
|
96
|
+
name = request.args.get("name", "Fenrir Developer")
|
|
97
|
+
return render_template("index.html", name=name)
|
|
98
|
+
|
|
99
|
+
# --- 3. Falcon-Style Class-Based Resources ---
|
|
100
|
+
class ItemResource:
|
|
101
|
+
async def on_get(self, req, resp, item_id: int):
|
|
102
|
+
resp.status = 200
|
|
103
|
+
resp.media = {"item_id": item_id, "style": "Falcon Resource"}
|
|
104
|
+
|
|
105
|
+
app.add_route("/items/<item_id:int>", ItemResource())
|
|
106
|
+
|
|
107
|
+
# --- 4. Sanic-Style Listeners & Background Tasks ---
|
|
108
|
+
@app.listener("before_server_start")
|
|
109
|
+
async def setup_db(app_instance):
|
|
110
|
+
logger.info("Initializing mock database...")
|
|
111
|
+
|
|
112
|
+
@app.middleware("request")
|
|
113
|
+
async def log_request(req):
|
|
114
|
+
logger.info(f"Incoming: {req.method} {req.path}")
|
|
115
|
+
g.user_role = "guest" # Share state using Flask-style 'g'
|
|
116
|
+
|
|
117
|
+
# Run with Asteri ASGI server
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
app.run(host="127.0.0.1", port=8000, workers=2)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 💻 CLI Command Reference
|
|
125
|
+
|
|
126
|
+
Fenrir comes packed with a high-fidelity, visually rich command-line tool. Start the CLI by executing `fenrir` or `python -m fenrir.cli`.
|
|
127
|
+
|
|
128
|
+
### 1. `fenrir run`
|
|
129
|
+
Serve your application locally. Powered by **Asteri v2.2.2**, supporting dynamic multiprocessing, worker management, and live hot-reloading.
|
|
130
|
+
```bash
|
|
131
|
+
fenrir run demo_app:app --port 8000 --dev
|
|
132
|
+
```
|
|
133
|
+
* **Flags**:
|
|
134
|
+
* `-H`, `--host`: Host bind address (default: `127.0.0.1`).
|
|
135
|
+
* `-p`, `--port`: Port number (default: `8000`).
|
|
136
|
+
* `-w`, `--workers`: Number of concurrent workers (default: `1`).
|
|
137
|
+
* `-d`, `--dev` / `--reload`: Active development mode with auto-reload.
|
|
138
|
+
|
|
139
|
+
### 2. `fenrir routes`
|
|
140
|
+
Print a beautiful, colorized structural table of all registered HTTP endpoints, methods, matching handlers, and associated blueprints.
|
|
141
|
+
```bash
|
|
142
|
+
fenrir routes demo_app:app
|
|
143
|
+
```
|
|
144
|
+
**Output Example:**
|
|
145
|
+
```text
|
|
146
|
+
----------------------------------------------------------------
|
|
147
|
+
Path Methods Handler Blueprint
|
|
148
|
+
----------------------------------------------------------------
|
|
149
|
+
/openapi.json GET, OPTIONS openapi_endpoint -
|
|
150
|
+
/docs GET, OPTIONS swagger_ui -
|
|
151
|
+
/ GET, OPTIONS home -
|
|
152
|
+
/items/<item_id:int> GET, POST ItemResource -
|
|
153
|
+
/api/register OPTIONS, POST register_user api
|
|
154
|
+
/ws/chat WEBSOCKET chat_ws -
|
|
155
|
+
----------------------------------------------------------------
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 3. `fenrir shell`
|
|
159
|
+
Instantly spawn an interactive python shell pre-configured with all key framework classes and context loaded (`app`, `request`, `g`, `Response`, `Blueprint`, etc.).
|
|
160
|
+
```bash
|
|
161
|
+
fenrir shell demo_app:app
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 4. `fenrir bench`
|
|
165
|
+
Perform in-memory framework benchmarking directly over ASGI using `HTTPX`. Eliminates network noise and tests raw pipeline speed under loaded constraints.
|
|
166
|
+
```bash
|
|
167
|
+
fenrir bench demo_app:app -i 1000 -t 5 -p / -m GET
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 5. `fenrir new`
|
|
171
|
+
Scaffold a complete, cleanly structured template of a new Fenrir project directory in seconds.
|
|
172
|
+
```bash
|
|
173
|
+
fenrir new my_new_project
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 6. `fenrir info`
|
|
177
|
+
Inspect the environment including Python details, OS details, Pydantic/Asteri versions, active compatibility layers, and route statistics.
|
|
178
|
+
```bash
|
|
179
|
+
fenrir info demo_app:app
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 🧪 Comprehensive Test Suite
|
|
185
|
+
|
|
186
|
+
Fenrir is thoroughly covered by an automated test suite comprising **478 tests** validating every single component, compat namespace, file upload, routing detail, and CLI functionality.
|
|
187
|
+
|
|
188
|
+
Run the test suite using `pytest`:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
pytest -v
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Output:
|
|
195
|
+
```text
|
|
196
|
+
======================== 477 passed, 1 skipped in 3.48s ========================
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 🛠️ Package Distribution (PyPI Publish Ready)
|
|
202
|
+
|
|
203
|
+
Fenrir is packed and ready for distribution.
|
|
204
|
+
1. Build the distribution binaries:
|
|
205
|
+
```bash
|
|
206
|
+
python -m build
|
|
207
|
+
```
|
|
208
|
+
2. Upload to PyPI using twine:
|
|
209
|
+
```bash
|
|
210
|
+
python -m twine upload dist/*
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 📜 License
|
|
216
|
+
|
|
217
|
+
Fenrir is open-sourced software licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="logo.jpg" alt="Fenrir Logo" width="300px"/>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# Fenrir Web Framework
|
|
6
|
+
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.python.org/)
|
|
9
|
+
[]()
|
|
10
|
+
[]()
|
|
11
|
+
|
|
12
|
+
**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.
|
|
13
|
+
|
|
14
|
+
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.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🌟 Key Features
|
|
19
|
+
|
|
20
|
+
* **⚡ High-Speed ASGI Core**: Extremely low-overhead routing and handler pipeline, achieving massive request throughput.
|
|
21
|
+
* **🧩 Framework Hybridization**:
|
|
22
|
+
* **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.
|
|
23
|
+
* **Flask Paradigm**: Thread/Task-safe context locals (`request`, `g`, `session`), Jinja2 template rendering (`render_template`), and request teardown hooks.
|
|
24
|
+
* **Falcon Paradigm**: Class-based resource controllers (`on_get`, `on_post`), before/after hooks, and in-place response mutation.
|
|
25
|
+
* **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`).
|
|
26
|
+
* **Bottle Paradigm**: Built-in WSGI-to-ASGI wrapper and legacy mount adapter (`app.mount_wsgi()`) to run old WSGI applications at ASGI speeds.
|
|
27
|
+
* **📖 Auto-Generated OpenAPI Docs**: Interactive **Swagger UI** (`/docs`) and **ReDoc** (`/redoc`) instantly generated from your Pydantic schemas and route metadata.
|
|
28
|
+
* **🔌 Modern Communications**: Out-of-the-box support for **WebSockets** and **Server-Sent Events (SSE)**.
|
|
29
|
+
* **🛠️ Premium CLI Tooling**: Visual route tables, interactive app shell, in-memory benchmarking suite, project scaffolding, and environment system inspection.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 📦 Installation
|
|
34
|
+
|
|
35
|
+
To install Fenrir in development mode, clone the repository and run:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install -e .
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
To install all optimal development and testing dependencies:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install pytest httpx watchdog pydantic jinja2 asteri>=2.2.2
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🚀 Quick Start (The Hybrid Power)
|
|
50
|
+
|
|
51
|
+
Here is a simple example (`demo_app.py`) showcasing how Flask, FastAPI, Falcon, and Sanic styles coexist harmoniously in a single application:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import logging
|
|
55
|
+
from pydantic import BaseModel
|
|
56
|
+
from fenrir import (
|
|
57
|
+
Fenrir, Blueprint, request, g, Depends, Query, Header,
|
|
58
|
+
render_template, Response, Form, File, UploadFile,
|
|
59
|
+
WebSocket, WebSocketDisconnect
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
logging.basicConfig(level=logging.INFO)
|
|
63
|
+
logger = logging.getLogger("demo")
|
|
64
|
+
|
|
65
|
+
# Initialize the Hybrid App
|
|
66
|
+
app = Fenrir(title="Fenrir Hybrid Demo", version="1.0.0")
|
|
67
|
+
|
|
68
|
+
# --- 1. FastAPI-Style Validation & DI ---
|
|
69
|
+
class UserRegister(BaseModel):
|
|
70
|
+
username: str
|
|
71
|
+
email: str
|
|
72
|
+
age: int
|
|
73
|
+
|
|
74
|
+
async def verify_api_key(x_api_key: str = Header(default=None)):
|
|
75
|
+
if x_api_key != "super-secret-key":
|
|
76
|
+
logger.warning("Invalid API key attempt")
|
|
77
|
+
return x_api_key
|
|
78
|
+
|
|
79
|
+
# --- 2. Flask-Style Routing & Context-Locals ---
|
|
80
|
+
@app.get("/")
|
|
81
|
+
async def home():
|
|
82
|
+
name = request.args.get("name", "Fenrir Developer")
|
|
83
|
+
return render_template("index.html", name=name)
|
|
84
|
+
|
|
85
|
+
# --- 3. Falcon-Style Class-Based Resources ---
|
|
86
|
+
class ItemResource:
|
|
87
|
+
async def on_get(self, req, resp, item_id: int):
|
|
88
|
+
resp.status = 200
|
|
89
|
+
resp.media = {"item_id": item_id, "style": "Falcon Resource"}
|
|
90
|
+
|
|
91
|
+
app.add_route("/items/<item_id:int>", ItemResource())
|
|
92
|
+
|
|
93
|
+
# --- 4. Sanic-Style Listeners & Background Tasks ---
|
|
94
|
+
@app.listener("before_server_start")
|
|
95
|
+
async def setup_db(app_instance):
|
|
96
|
+
logger.info("Initializing mock database...")
|
|
97
|
+
|
|
98
|
+
@app.middleware("request")
|
|
99
|
+
async def log_request(req):
|
|
100
|
+
logger.info(f"Incoming: {req.method} {req.path}")
|
|
101
|
+
g.user_role = "guest" # Share state using Flask-style 'g'
|
|
102
|
+
|
|
103
|
+
# Run with Asteri ASGI server
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
app.run(host="127.0.0.1", port=8000, workers=2)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 💻 CLI Command Reference
|
|
111
|
+
|
|
112
|
+
Fenrir comes packed with a high-fidelity, visually rich command-line tool. Start the CLI by executing `fenrir` or `python -m fenrir.cli`.
|
|
113
|
+
|
|
114
|
+
### 1. `fenrir run`
|
|
115
|
+
Serve your application locally. Powered by **Asteri v2.2.2**, supporting dynamic multiprocessing, worker management, and live hot-reloading.
|
|
116
|
+
```bash
|
|
117
|
+
fenrir run demo_app:app --port 8000 --dev
|
|
118
|
+
```
|
|
119
|
+
* **Flags**:
|
|
120
|
+
* `-H`, `--host`: Host bind address (default: `127.0.0.1`).
|
|
121
|
+
* `-p`, `--port`: Port number (default: `8000`).
|
|
122
|
+
* `-w`, `--workers`: Number of concurrent workers (default: `1`).
|
|
123
|
+
* `-d`, `--dev` / `--reload`: Active development mode with auto-reload.
|
|
124
|
+
|
|
125
|
+
### 2. `fenrir routes`
|
|
126
|
+
Print a beautiful, colorized structural table of all registered HTTP endpoints, methods, matching handlers, and associated blueprints.
|
|
127
|
+
```bash
|
|
128
|
+
fenrir routes demo_app:app
|
|
129
|
+
```
|
|
130
|
+
**Output Example:**
|
|
131
|
+
```text
|
|
132
|
+
----------------------------------------------------------------
|
|
133
|
+
Path Methods Handler Blueprint
|
|
134
|
+
----------------------------------------------------------------
|
|
135
|
+
/openapi.json GET, OPTIONS openapi_endpoint -
|
|
136
|
+
/docs GET, OPTIONS swagger_ui -
|
|
137
|
+
/ GET, OPTIONS home -
|
|
138
|
+
/items/<item_id:int> GET, POST ItemResource -
|
|
139
|
+
/api/register OPTIONS, POST register_user api
|
|
140
|
+
/ws/chat WEBSOCKET chat_ws -
|
|
141
|
+
----------------------------------------------------------------
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 3. `fenrir shell`
|
|
145
|
+
Instantly spawn an interactive python shell pre-configured with all key framework classes and context loaded (`app`, `request`, `g`, `Response`, `Blueprint`, etc.).
|
|
146
|
+
```bash
|
|
147
|
+
fenrir shell demo_app:app
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 4. `fenrir bench`
|
|
151
|
+
Perform in-memory framework benchmarking directly over ASGI using `HTTPX`. Eliminates network noise and tests raw pipeline speed under loaded constraints.
|
|
152
|
+
```bash
|
|
153
|
+
fenrir bench demo_app:app -i 1000 -t 5 -p / -m GET
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 5. `fenrir new`
|
|
157
|
+
Scaffold a complete, cleanly structured template of a new Fenrir project directory in seconds.
|
|
158
|
+
```bash
|
|
159
|
+
fenrir new my_new_project
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 6. `fenrir info`
|
|
163
|
+
Inspect the environment including Python details, OS details, Pydantic/Asteri versions, active compatibility layers, and route statistics.
|
|
164
|
+
```bash
|
|
165
|
+
fenrir info demo_app:app
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 🧪 Comprehensive Test Suite
|
|
171
|
+
|
|
172
|
+
Fenrir is thoroughly covered by an automated test suite comprising **478 tests** validating every single component, compat namespace, file upload, routing detail, and CLI functionality.
|
|
173
|
+
|
|
174
|
+
Run the test suite using `pytest`:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
pytest -v
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Output:
|
|
181
|
+
```text
|
|
182
|
+
======================== 477 passed, 1 skipped in 3.48s ========================
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 🛠️ Package Distribution (PyPI Publish Ready)
|
|
188
|
+
|
|
189
|
+
Fenrir is packed and ready for distribution.
|
|
190
|
+
1. Build the distribution binaries:
|
|
191
|
+
```bash
|
|
192
|
+
python -m build
|
|
193
|
+
```
|
|
194
|
+
2. Upload to PyPI using twine:
|
|
195
|
+
```bash
|
|
196
|
+
python -m twine upload dist/*
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 📜 License
|
|
202
|
+
|
|
203
|
+
Fenrir is open-sourced software licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from fenrir.app import Fenrir, Blueprint
|
|
2
|
+
from fenrir.context import request, g, current_app, session
|
|
3
|
+
from fenrir.dependencies import Depends, Query, Header, Cookie, Body, Path, Form, File
|
|
4
|
+
from fenrir.upload import UploadFile
|
|
5
|
+
from fenrir.websocket import WebSocket, WebSocketDisconnect
|
|
6
|
+
from fenrir.exceptions import (
|
|
7
|
+
HTTPException,
|
|
8
|
+
HTTPBadRequest,
|
|
9
|
+
HTTPUnauthorized,
|
|
10
|
+
HTTPForbidden,
|
|
11
|
+
HTTPNotFound,
|
|
12
|
+
HTTPMethodNotAllowed,
|
|
13
|
+
HTTPConflict,
|
|
14
|
+
HTTPUnprocessableEntity,
|
|
15
|
+
HTTPInternalServerError,
|
|
16
|
+
)
|
|
17
|
+
from fenrir.response import (
|
|
18
|
+
Response,
|
|
19
|
+
JSONResponse,
|
|
20
|
+
HTMLResponse,
|
|
21
|
+
TextResponse,
|
|
22
|
+
RedirectResponse,
|
|
23
|
+
StreamingResponse,
|
|
24
|
+
FileResponse,
|
|
25
|
+
PlainTextResponse,
|
|
26
|
+
)
|
|
27
|
+
from fenrir.templating import render_template, BaseTemplateRenderer, Jinja2Renderer
|
|
28
|
+
from fenrir.views import View, MethodView
|
|
29
|
+
from fenrir.helpers import url_for, send_file, send_from_directory, redirect
|
|
30
|
+
from fenrir.routing import Router, Route, APIRouter
|
|
31
|
+
from fenrir.sse import EventSourceResponse
|
|
32
|
+
from fenrir.security import (
|
|
33
|
+
APIKeyCookie,
|
|
34
|
+
APIKeyHeader,
|
|
35
|
+
APIKeyQuery,
|
|
36
|
+
HTTPBasic,
|
|
37
|
+
HTTPBearer,
|
|
38
|
+
HTTPDigest,
|
|
39
|
+
OAuth2PasswordBearer,
|
|
40
|
+
OAuth2AuthorizationCodeBearer,
|
|
41
|
+
OpenIDConnect,
|
|
42
|
+
)
|
|
43
|
+
from fenrir.background import BackgroundTasks, BackgroundTask
|
|
44
|
+
from fenrir.compat import WsgiToAsgi, install_bottle_compat, install_falcon_compat, install_sanic_compat
|
|
45
|
+
from fenrir.bottle import Bottle
|
|
46
|
+
import fenrir.bottle as bottle
|
|
47
|
+
import fenrir.falcon as falcon
|
|
48
|
+
import fenrir.sanic as sanic
|
|
49
|
+
from fenrir.testing import TestClient, FenrirTestClient
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Re-export Annotated for convenient use with param markers
|
|
55
|
+
from typing import Annotated
|
|
56
|
+
|
|
57
|
+
__version__ = "0.1.0"
|
|
58
|
+
__all__ = [
|
|
59
|
+
# Core app
|
|
60
|
+
"Fenrir",
|
|
61
|
+
"Blueprint",
|
|
62
|
+
# Context
|
|
63
|
+
"request",
|
|
64
|
+
"g",
|
|
65
|
+
"current_app",
|
|
66
|
+
"session",
|
|
67
|
+
# Dependency injection
|
|
68
|
+
"Depends",
|
|
69
|
+
"Query",
|
|
70
|
+
"Header",
|
|
71
|
+
"Cookie",
|
|
72
|
+
"Body",
|
|
73
|
+
"Path",
|
|
74
|
+
"Form",
|
|
75
|
+
"File",
|
|
76
|
+
"Annotated",
|
|
77
|
+
# File upload
|
|
78
|
+
"UploadFile",
|
|
79
|
+
# WebSocket
|
|
80
|
+
"WebSocket",
|
|
81
|
+
"WebSocketDisconnect",
|
|
82
|
+
# Exceptions
|
|
83
|
+
"HTTPException",
|
|
84
|
+
"HTTPBadRequest",
|
|
85
|
+
"HTTPUnauthorized",
|
|
86
|
+
"HTTPForbidden",
|
|
87
|
+
"HTTPNotFound",
|
|
88
|
+
"HTTPMethodNotAllowed",
|
|
89
|
+
"HTTPConflict",
|
|
90
|
+
"HTTPUnprocessableEntity",
|
|
91
|
+
"HTTPInternalServerError",
|
|
92
|
+
# Responses
|
|
93
|
+
"Response",
|
|
94
|
+
"JSONResponse",
|
|
95
|
+
"HTMLResponse",
|
|
96
|
+
"TextResponse",
|
|
97
|
+
"RedirectResponse",
|
|
98
|
+
"StreamingResponse",
|
|
99
|
+
"FileResponse",
|
|
100
|
+
"PlainTextResponse",
|
|
101
|
+
# Templating
|
|
102
|
+
"render_template",
|
|
103
|
+
"BaseTemplateRenderer",
|
|
104
|
+
"Jinja2Renderer",
|
|
105
|
+
# Views
|
|
106
|
+
"View",
|
|
107
|
+
"MethodView",
|
|
108
|
+
# Helpers
|
|
109
|
+
"url_for",
|
|
110
|
+
"send_file",
|
|
111
|
+
"send_from_directory",
|
|
112
|
+
"redirect",
|
|
113
|
+
# Routing
|
|
114
|
+
"Router",
|
|
115
|
+
"Route",
|
|
116
|
+
"APIRouter",
|
|
117
|
+
# SSE
|
|
118
|
+
"EventSourceResponse",
|
|
119
|
+
# Security
|
|
120
|
+
"APIKeyCookie",
|
|
121
|
+
"APIKeyHeader",
|
|
122
|
+
"APIKeyQuery",
|
|
123
|
+
"HTTPBasic",
|
|
124
|
+
"HTTPBearer",
|
|
125
|
+
"HTTPDigest",
|
|
126
|
+
"OAuth2PasswordBearer",
|
|
127
|
+
"OAuth2AuthorizationCodeBearer",
|
|
128
|
+
"OpenIDConnect",
|
|
129
|
+
# Background tasks
|
|
130
|
+
"BackgroundTasks",
|
|
131
|
+
"BackgroundTask",
|
|
132
|
+
# WSGI/Falcon/Sanic compat
|
|
133
|
+
"WsgiToAsgi",
|
|
134
|
+
"install_bottle_compat",
|
|
135
|
+
"install_falcon_compat",
|
|
136
|
+
"install_sanic_compat",
|
|
137
|
+
# Bottle built-in
|
|
138
|
+
"Bottle",
|
|
139
|
+
"bottle",
|
|
140
|
+
# Falcon built-in
|
|
141
|
+
"falcon",
|
|
142
|
+
# Sanic built-in
|
|
143
|
+
"sanic",
|
|
144
|
+
# Testing
|
|
145
|
+
"TestClient",
|
|
146
|
+
"FenrirTestClient",
|
|
147
|
+
]
|
|
148
|
+
|