fluxium 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.
- fluxium-0.1.0/CODE_OF_CONDUCT.md +30 -0
- fluxium-0.1.0/CONTRIBUTING.md +92 -0
- fluxium-0.1.0/LICENSE +21 -0
- fluxium-0.1.0/MANIFEST.in +8 -0
- fluxium-0.1.0/PKG-INFO +408 -0
- fluxium-0.1.0/README.md +371 -0
- fluxium-0.1.0/docs/architecture.md +205 -0
- fluxium-0.1.0/docs/getting_started.md +368 -0
- fluxium-0.1.0/docs/internals.md +198 -0
- fluxium-0.1.0/examples/example_app.py +234 -0
- fluxium-0.1.0/examples/fullstack_app.py +250 -0
- fluxium-0.1.0/examples/taskflow_demo.py +1238 -0
- fluxium-0.1.0/flux/__init__.py +54 -0
- fluxium-0.1.0/flux/__main__.py +4 -0
- fluxium-0.1.0/flux/auth/__init__.py +14 -0
- fluxium-0.1.0/flux/auth/auth.py +332 -0
- fluxium-0.1.0/flux/benchmarking.py +290 -0
- fluxium-0.1.0/flux/cli.py +199 -0
- fluxium-0.1.0/flux/client/__init__.py +16 -0
- fluxium-0.1.0/flux/client/client.py +165 -0
- fluxium-0.1.0/flux/client/response.py +104 -0
- fluxium-0.1.0/flux/client/retry.py +80 -0
- fluxium-0.1.0/flux/client/session.py +315 -0
- fluxium-0.1.0/flux/config/__init__.py +4 -0
- fluxium-0.1.0/flux/config/settings.py +189 -0
- fluxium-0.1.0/flux/core/__init__.py +36 -0
- fluxium-0.1.0/flux/core/connection.py +214 -0
- fluxium-0.1.0/flux/core/headers.py +147 -0
- fluxium-0.1.0/flux/core/json.py +84 -0
- fluxium-0.1.0/flux/core/parser.py +388 -0
- fluxium-0.1.0/flux/core/pool.py +209 -0
- fluxium-0.1.0/flux/core/streaming.py +168 -0
- fluxium-0.1.0/flux/observability/__init__.py +18 -0
- fluxium-0.1.0/flux/observability/metrics.py +257 -0
- fluxium-0.1.0/flux/openapi/__init__.py +4 -0
- fluxium-0.1.0/flux/openapi/generator.py +215 -0
- fluxium-0.1.0/flux/orm/__init__.py +13 -0
- fluxium-0.1.0/flux/orm/migrations.py +112 -0
- fluxium-0.1.0/flux/orm/model.py +517 -0
- fluxium-0.1.0/flux/plugins/__init__.py +10 -0
- fluxium-0.1.0/flux/plugins/plugin.py +194 -0
- fluxium-0.1.0/flux/py.typed +0 -0
- fluxium-0.1.0/flux/scaffold.py +285 -0
- fluxium-0.1.0/flux/schema/__init__.py +4 -0
- fluxium-0.1.0/flux/schema/schema.py +404 -0
- fluxium-0.1.0/flux/security/__init__.py +20 -0
- fluxium-0.1.0/flux/security/security.py +254 -0
- fluxium-0.1.0/flux/server/__init__.py +39 -0
- fluxium-0.1.0/flux/server/app.py +319 -0
- fluxium-0.1.0/flux/server/context.py +62 -0
- fluxium-0.1.0/flux/server/di.py +191 -0
- fluxium-0.1.0/flux/server/middleware.py +153 -0
- fluxium-0.1.0/flux/server/request.py +180 -0
- fluxium-0.1.0/flux/server/response.py +281 -0
- fluxium-0.1.0/flux/server/router.py +163 -0
- fluxium-0.1.0/flux/tasks/__init__.py +4 -0
- fluxium-0.1.0/flux/tasks/queue.py +262 -0
- fluxium-0.1.0/flux/templates/__init__.py +15 -0
- fluxium-0.1.0/flux/templates/engine.py +184 -0
- fluxium-0.1.0/flux/websocket/__init__.py +4 -0
- fluxium-0.1.0/flux/websocket/websocket.py +258 -0
- fluxium-0.1.0/fluxium.egg-info/PKG-INFO +408 -0
- fluxium-0.1.0/fluxium.egg-info/SOURCES.txt +75 -0
- fluxium-0.1.0/fluxium.egg-info/dependency_links.txt +1 -0
- fluxium-0.1.0/fluxium.egg-info/entry_points.txt +3 -0
- fluxium-0.1.0/fluxium.egg-info/requires.txt +16 -0
- fluxium-0.1.0/fluxium.egg-info/top_level.txt +1 -0
- fluxium-0.1.0/pyproject.toml +62 -0
- fluxium-0.1.0/setup.cfg +7 -0
- fluxium-0.1.0/tests/test_auth.py +74 -0
- fluxium-0.1.0/tests/test_integration.py +184 -0
- fluxium-0.1.0/tests/test_middleware.py +88 -0
- fluxium-0.1.0/tests/test_orm.py +140 -0
- fluxium-0.1.0/tests/test_parser.py +70 -0
- fluxium-0.1.0/tests/test_router.py +79 -0
- fluxium-0.1.0/tests/test_schema.py +100 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We are committed to making participation in this project a welcoming experience for everyone, regardless of background, experience level, gender, identity, nationality, ethnicity, religion, or any other personal characteristic.
|
|
6
|
+
|
|
7
|
+
## Our Standards
|
|
8
|
+
|
|
9
|
+
**Positive behaviour includes:**
|
|
10
|
+
|
|
11
|
+
- Using welcoming and inclusive language
|
|
12
|
+
- Being respectful of differing viewpoints and experience
|
|
13
|
+
- Gracefully accepting constructive feedback
|
|
14
|
+
- Focusing on what is best for the community
|
|
15
|
+
- Showing empathy toward other community members
|
|
16
|
+
|
|
17
|
+
**Unacceptable behaviour includes:**
|
|
18
|
+
|
|
19
|
+
- Harassment or discriminatory language of any kind
|
|
20
|
+
- Personal attacks or insults
|
|
21
|
+
- Trolling or deliberately disruptive behaviour
|
|
22
|
+
- Publishing others' private information without permission
|
|
23
|
+
|
|
24
|
+
## Enforcement
|
|
25
|
+
|
|
26
|
+
Instances of unacceptable behaviour may be reported by opening a private issue or contacting the maintainers directly. All reports will be reviewed and addressed promptly.
|
|
27
|
+
|
|
28
|
+
## Attribution
|
|
29
|
+
|
|
30
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Contributing to Fluxium
|
|
2
|
+
|
|
3
|
+
Fluxium is primarily a learning project, but contributions are welcome. If you have found a bug, want to improve performance, or want to add something useful, open an issue first so we can talk through the approach before you write code.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Getting started
|
|
8
|
+
|
|
9
|
+
Fork the repository and clone it:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
git clone https://github.com/sidx0/fluxium
|
|
13
|
+
cd fluxium
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Set up a virtual environment:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
python -m venv venv
|
|
20
|
+
source venv/bin/activate # Windows: venv\Scripts\activate
|
|
21
|
+
pip install -e ".[dev]"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Run the tests to confirm everything is working:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pytest tests/ -v
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## What to work on
|
|
33
|
+
|
|
34
|
+
Bug fixes are always welcome, especially in the HTTP parser, ORM, or DI system. If you have a performance improvement, include benchmarks before and after so the change can be evaluated. New layers should follow the pattern of existing ones — zero mandatory dependencies, testable in isolation.
|
|
35
|
+
|
|
36
|
+
If you want to add a feature, open an issue describing what problem it solves before writing code. That way you do not spend time on something that might not fit the project's direction.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Making a change
|
|
41
|
+
|
|
42
|
+
Create a branch:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git checkout -b fix/describe-the-fix
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Write tests for your change. Tests are in `tests/` and use pytest-asyncio for async cases:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import pytest
|
|
52
|
+
from flux import Flux
|
|
53
|
+
|
|
54
|
+
@pytest.mark.asyncio
|
|
55
|
+
async def test_something():
|
|
56
|
+
app = Flux()
|
|
57
|
+
...
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Make sure the full test suite passes before submitting:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pytest tests/ -v
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Pull request checklist
|
|
69
|
+
|
|
70
|
+
- Tests pass
|
|
71
|
+
- New behaviour has test coverage
|
|
72
|
+
- No new mandatory dependencies
|
|
73
|
+
- The PR description explains what the change does and why
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Reporting bugs
|
|
78
|
+
|
|
79
|
+
Open an issue with:
|
|
80
|
+
|
|
81
|
+
- Fluxium version (`fluxium version`)
|
|
82
|
+
- Python version and OS
|
|
83
|
+
- A minimal example that reproduces the problem
|
|
84
|
+
- The full traceback if there is one
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Code style
|
|
89
|
+
|
|
90
|
+
Type hints on all public functions. `async def` for anything that does I/O. Keep modules focused — each file should do one thing. Docstrings on public classes and functions.
|
|
91
|
+
|
|
92
|
+
No external formatters are required, but the code should be consistent with what is already there.
|
fluxium-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Flux Contributors
|
|
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.
|
fluxium-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fluxium
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A zero-dependency async Python web framework
|
|
5
|
+
Project-URL: Homepage, https://github.com/sidx0/fluxium
|
|
6
|
+
Project-URL: Repository, https://github.com/sidx0/fluxium
|
|
7
|
+
Project-URL: Documentation, https://github.com/sidx0/fluxium/tree/main/docs
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/sidx0/fluxium/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/sidx0/fluxium/blob/main/CHANGELOG.md
|
|
10
|
+
Keywords: web,framework,asyncio,http,async,backend
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Framework :: AsyncIO
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
22
|
+
Classifier: Operating System :: OS Independent
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Provides-Extra: jwt
|
|
26
|
+
Requires-Dist: PyJWT>=2.8; extra == "jwt"
|
|
27
|
+
Provides-Extra: templates
|
|
28
|
+
Requires-Dist: Jinja2>=3.1; extra == "templates"
|
|
29
|
+
Provides-Extra: all
|
|
30
|
+
Requires-Dist: PyJWT>=2.8; extra == "all"
|
|
31
|
+
Requires-Dist: Jinja2>=3.1; extra == "all"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
35
|
+
Requires-Dist: PyJWT>=2.8; extra == "dev"
|
|
36
|
+
Requires-Dist: Jinja2>=3.1; extra == "dev"
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<img src="logo.png" alt="Fluxium" width="220">
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<h1 align="center">Fluxium</h1>
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
An async Python web framework written from scratch.<br>
|
|
46
|
+
No Starlette. No Pydantic. No Uvicorn. Just Python.
|
|
47
|
+
</p>
|
|
48
|
+
|
|
49
|
+
<p align="center">
|
|
50
|
+
<a href="https://pypi.org/project/fluxium"><img src="https://img.shields.io/pypi/v/fluxium?color=1a1a1a&style=flat-square" alt="PyPI"></a>
|
|
51
|
+
<a href="https://github.com/sidx0/fluxium/actions"><img src="https://github.com/sidx0/fluxium/actions/workflows/tests.yml/badge.svg" alt="Tests"></a>
|
|
52
|
+
<img src="https://img.shields.io/badge/python-3.10+-1a1a1a?style=flat-square" alt="Python">
|
|
53
|
+
<img src="https://img.shields.io/badge/dependencies-zero-d4a017?style=flat-square" alt="Zero dependencies">
|
|
54
|
+
<img src="https://img.shields.io/badge/license-MIT-1a1a1a?style=flat-square" alt="MIT">
|
|
55
|
+
</p>
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
Fluxium implements the entire HTTP stack in pure Python: the parser, header container, connection pool, router, middleware pipeline, dependency injection engine, ORM, authentication, WebSocket support, background task queue, and Prometheus metrics — all without a single mandatory third-party dependency.
|
|
60
|
+
|
|
61
|
+
It started as an experiment to understand what a web framework actually does underneath. The result is fast, readable at every layer, and ships more built-in functionality than most frameworks that rely on an ecosystem of external packages.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from flux import Flux
|
|
65
|
+
|
|
66
|
+
app = Flux()
|
|
67
|
+
|
|
68
|
+
@app.get("/")
|
|
69
|
+
async def index():
|
|
70
|
+
return {"message": "Hello from Fluxium"}
|
|
71
|
+
|
|
72
|
+
app.run(port=8000)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install fluxium
|
|
77
|
+
fluxium new myproject
|
|
78
|
+
cd myproject && python main.py
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Design
|
|
84
|
+
|
|
85
|
+
Most Python frameworks are layers on top of other layers. FastAPI wraps Starlette. Starlette wraps anyio and h11. The code that actually moves bytes around lives several packages away from the thing you called `pip install` on.
|
|
86
|
+
|
|
87
|
+
Fluxium owns the whole stack. The HTTP parser uses `memoryview` slices to avoid copying bytes during header parsing. The router compiles route patterns into named-group regexes at registration time and caches matches in a dictionary. The middleware pipeline is built as a flat chain of closures rather than a recursive call stack. The in-process loopback client calls the app's dispatch function directly, skipping TCP and serialisation entirely.
|
|
88
|
+
|
|
89
|
+
Everything has a reason. Everything is readable.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Performance
|
|
94
|
+
|
|
95
|
+
Benchmarks run in-process on a single core. No real TCP.
|
|
96
|
+
|
|
97
|
+
| Operation | Throughput | Mean latency |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| HTTP parse, GET | 140,026 / sec | 7.1 µs |
|
|
100
|
+
| HTTP parse, POST + JSON body | 124,527 / sec | 8.0 µs |
|
|
101
|
+
| Route match, warm cache | 986,564 / sec | 1.0 µs |
|
|
102
|
+
| JSONResponse build and encode | 222,480 / sec | 4.5 µs |
|
|
103
|
+
| Full in-process dispatch | 39,171 / sec | 25.5 µs |
|
|
104
|
+
|
|
105
|
+
Compared to Flask on the same in-process test harness:
|
|
106
|
+
|
|
107
|
+
| Metric | Flask | Fluxium |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| GET dispatch | 5,831 / sec | 70,142 / sec |
|
|
110
|
+
| URL routing | 206,654 / sec | 6,367,537 / sec |
|
|
111
|
+
| Memory per request | 12 KB | 4 KB |
|
|
112
|
+
| Import time | 255 ms | 16 ms |
|
|
113
|
+
|
|
114
|
+
The gap comes from abstraction count, not optimization tricks. Flask builds a full Werkzeug request context on every request. Fluxium wraps a parsed bytearray.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
python -m flux benchmark
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Installation
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install fluxium
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Optional extras:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
pip install "fluxium[jwt]" # PyJWT for token signing
|
|
132
|
+
pip install "fluxium[templates]" # Jinja2 for template rendering
|
|
133
|
+
pip install "fluxium[all]" # both
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Neither extra is required. JWT falls back to stdlib HMAC-SHA256. Templates fall back to a built-in engine.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Features
|
|
141
|
+
|
|
142
|
+
### Core engine
|
|
143
|
+
|
|
144
|
+
- HTTP/1.1 parser with zero-copy header parsing via `memoryview`
|
|
145
|
+
- Compiled regex router with typed path parameters — `{id:int}`, `{slug:str}`, `{uid:uuid}`, `{file:path}`
|
|
146
|
+
- Route match cache — warm lookups at roughly one million per second
|
|
147
|
+
- Non-recursive async middleware pipeline, safe at any depth
|
|
148
|
+
- Type-hint driven dependency injection with per-request scope
|
|
149
|
+
- Schema auto-injection — body is parsed and validated before the handler runs
|
|
150
|
+
- Async connection pool with keep-alive and idle reaping
|
|
151
|
+
- In-process loopback client — same-process calls skip TCP entirely
|
|
152
|
+
|
|
153
|
+
### Full-stack layers
|
|
154
|
+
|
|
155
|
+
Every layer runs on Python's standard library. Optional dependencies improve but never block.
|
|
156
|
+
|
|
157
|
+
| Layer | Description |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `flux.orm` | Async SQLite ORM. Model, QuerySet, auto-migrations, thread-safe transactions. |
|
|
160
|
+
| `flux.auth` | JWT tokens, PBKDF2 password hashing, `@requires_auth`, `@requires_role`. |
|
|
161
|
+
| `flux.schema` | Validation with type coercion, constraints, mutable-default safety, 422 auto-response. |
|
|
162
|
+
| `flux.openapi` | Auto-generated OpenAPI 3.0 spec. Swagger UI at `/docs`, ReDoc at `/redoc`. |
|
|
163
|
+
| `flux.websocket` | RFC 6455 WebSocket with full fragmented-frame reassembly. |
|
|
164
|
+
| `flux.tasks` | Async priority task queue with configurable retry and exponential backoff. |
|
|
165
|
+
| `flux.observability` | Prometheus-compatible counters, histograms, structured JSON logging. |
|
|
166
|
+
| `flux.config` | `Settings` class that reads from environment variables and `.env` files with type coercion. |
|
|
167
|
+
| `flux.security` | Token-bucket rate limiter, CSRF protection, security headers, HTML sanitizer. |
|
|
168
|
+
| `flux.templates` | Jinja2 integration with a built-in fallback engine. |
|
|
169
|
+
| `flux.plugins` | Plugin lifecycle — `on_install`, `on_startup`, `on_shutdown`. |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Usage
|
|
174
|
+
|
|
175
|
+
### Path parameters
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
@app.get("/users/{id:int}")
|
|
179
|
+
async def get_user(id: int):
|
|
180
|
+
return {"id": id}
|
|
181
|
+
|
|
182
|
+
@app.get("/files/{path:path}")
|
|
183
|
+
async def get_file(path: str):
|
|
184
|
+
return {"path": path}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Supported types: `int`, `float`, `str` (default), `path`, `uuid`. A non-matching value returns 404 — no manual validation needed.
|
|
188
|
+
|
|
189
|
+
### Request body validation
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from flux.schema import Schema, Field
|
|
193
|
+
from typing import Optional
|
|
194
|
+
|
|
195
|
+
class CreatePost(Schema):
|
|
196
|
+
title: str = Field(min_length=1, max_length=200)
|
|
197
|
+
body: str = Field(default="")
|
|
198
|
+
priority: int = Field(default=1, min_value=1, max_value=5)
|
|
199
|
+
tags: list = Field(default=[])
|
|
200
|
+
|
|
201
|
+
@app.post("/posts")
|
|
202
|
+
async def create_post(data: CreatePost):
|
|
203
|
+
return {"title": data.title, "priority": data.priority}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
If validation fails the handler is not called. The client receives a structured 422 response listing every field that failed and why.
|
|
207
|
+
|
|
208
|
+
### Authentication
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from flux.auth import AuthConfig, AuthUser, requires_auth, requires_role
|
|
212
|
+
|
|
213
|
+
app.auth = AuthConfig(secret="your-secret-key", token_ttl=86400)
|
|
214
|
+
|
|
215
|
+
@app.post("/login")
|
|
216
|
+
async def login(request):
|
|
217
|
+
data = request.json()
|
|
218
|
+
user = await User.filter(username=data["username"]).first()
|
|
219
|
+
if not user or not verify_password(data["password"], user.password_hash):
|
|
220
|
+
from flux.server.response import ErrorResponse
|
|
221
|
+
return ErrorResponse(401, "Invalid credentials")
|
|
222
|
+
token = app.auth.issue_token(user.id, roles=[user.role], username=user.username)
|
|
223
|
+
return {"token": token}
|
|
224
|
+
|
|
225
|
+
@app.get("/me")
|
|
226
|
+
@requires_auth
|
|
227
|
+
async def me(user: AuthUser):
|
|
228
|
+
return {"id": user.id, "username": user.username}
|
|
229
|
+
|
|
230
|
+
@app.delete("/admin/users/{id:int}")
|
|
231
|
+
@requires_role("admin")
|
|
232
|
+
async def delete_user(id: int, user: AuthUser):
|
|
233
|
+
await User.filter(id=id).delete()
|
|
234
|
+
return None
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Database
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
from flux.orm import Database, Model, Integer, String, Boolean, auto_migrate
|
|
241
|
+
|
|
242
|
+
db = Database("sqlite:///app.db")
|
|
243
|
+
|
|
244
|
+
class Post(Model):
|
|
245
|
+
id = Integer(primary_key=True)
|
|
246
|
+
title = String(nullable=False)
|
|
247
|
+
status = String(default="draft")
|
|
248
|
+
visible = Boolean(default=True)
|
|
249
|
+
|
|
250
|
+
Post.bind(db)
|
|
251
|
+
|
|
252
|
+
@app.on_startup
|
|
253
|
+
async def startup():
|
|
254
|
+
await auto_migrate(Post)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
post = await Post.create(title="First post")
|
|
259
|
+
posts = await Post.filter(status="draft").order_by("-id").limit(20).all()
|
|
260
|
+
count = await Post.all().count()
|
|
261
|
+
|
|
262
|
+
await Post.filter(id=post.id).update(status="published")
|
|
263
|
+
await post.delete()
|
|
264
|
+
|
|
265
|
+
async with db.transaction():
|
|
266
|
+
await Account.filter(id=from_id).update(balance=new_balance_from)
|
|
267
|
+
await Account.filter(id=to_id).update(balance=new_balance_to)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Background tasks
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
from flux.tasks import TaskQueue
|
|
274
|
+
|
|
275
|
+
tq = TaskQueue(workers=4)
|
|
276
|
+
app.di.register(TaskQueue, lambda: tq)
|
|
277
|
+
|
|
278
|
+
@app.on_startup
|
|
279
|
+
async def startup():
|
|
280
|
+
await tq.start()
|
|
281
|
+
|
|
282
|
+
@app.on_shutdown
|
|
283
|
+
async def shutdown():
|
|
284
|
+
await tq.stop(wait=True)
|
|
285
|
+
|
|
286
|
+
@app.post("/register")
|
|
287
|
+
async def register(data: RegisterInput, tq: TaskQueue):
|
|
288
|
+
user = await User.create(...)
|
|
289
|
+
await tq.enqueue(send_welcome_email, user.email)
|
|
290
|
+
return JSONResponse(user.to_dict(), status_code=201)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### WebSocket
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from flux.websocket import WebSocket, WebSocketDisconnect
|
|
297
|
+
|
|
298
|
+
@app.websocket("/ws")
|
|
299
|
+
async def handler(ws: WebSocket):
|
|
300
|
+
await ws.accept()
|
|
301
|
+
try:
|
|
302
|
+
while True:
|
|
303
|
+
message = await ws.receive_text()
|
|
304
|
+
await ws.send_json({"echo": message})
|
|
305
|
+
except WebSocketDisconnect:
|
|
306
|
+
pass
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Full application setup
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
from flux import Flux
|
|
313
|
+
from flux.auth import AuthConfig
|
|
314
|
+
from flux.plugins import CORSPlugin
|
|
315
|
+
from flux.observability import observability_middleware, mount_metrics
|
|
316
|
+
from flux.security import RateLimiter, security_headers_middleware
|
|
317
|
+
|
|
318
|
+
app = Flux(title="My API", version="1.0.0")
|
|
319
|
+
app.auth = AuthConfig(secret="change-in-production")
|
|
320
|
+
|
|
321
|
+
app.install(CORSPlugin(allow_origins="*"))
|
|
322
|
+
app.add_middleware(observability_middleware)
|
|
323
|
+
app.add_middleware(RateLimiter(requests_per_second=100))
|
|
324
|
+
app.add_middleware(security_headers_middleware)
|
|
325
|
+
mount_metrics(app)
|
|
326
|
+
app.mount_docs()
|
|
327
|
+
|
|
328
|
+
app.run(port=8000)
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## CLI
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
fluxium new myapp # scaffold a complete project
|
|
337
|
+
fluxium run myapp:app # start the development server
|
|
338
|
+
fluxium run myapp:app --reload # with hot reload on file changes
|
|
339
|
+
fluxium routes myapp:app # print all registered routes
|
|
340
|
+
fluxium version
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
`flux` works as an alias for all commands.
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Project layout
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
fluxium/
|
|
351
|
+
flux/
|
|
352
|
+
core/ HTTP parser, headers, connection pool
|
|
353
|
+
server/ Router, middleware, DI, request, response
|
|
354
|
+
client/ Async HTTP client and loopback
|
|
355
|
+
orm/ SQLite ORM
|
|
356
|
+
auth/ JWT, password hashing, RBAC
|
|
357
|
+
schema/ Validation engine
|
|
358
|
+
openapi/ OpenAPI spec and UI
|
|
359
|
+
websocket/ RFC 6455 implementation
|
|
360
|
+
tasks/ Background task queue
|
|
361
|
+
observability/ Prometheus metrics and structured logging
|
|
362
|
+
config/ Environment-based configuration
|
|
363
|
+
security/ Rate limiting, CSRF, security headers
|
|
364
|
+
templates/ Template rendering
|
|
365
|
+
plugins/ Plugin lifecycle
|
|
366
|
+
examples/
|
|
367
|
+
example_app.py Minimal working example
|
|
368
|
+
fullstack_app.py All layers in one file
|
|
369
|
+
taskflow_demo.py Complete task manager with browser UI
|
|
370
|
+
tests/
|
|
371
|
+
docs/
|
|
372
|
+
architecture.md
|
|
373
|
+
getting_started.md
|
|
374
|
+
internals.md
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Documentation
|
|
380
|
+
|
|
381
|
+
- [Getting Started](docs/getting_started.md)
|
|
382
|
+
- [Architecture](docs/architecture.md)
|
|
383
|
+
- [Internals](docs/internals.md)
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Requirements
|
|
388
|
+
|
|
389
|
+
- Python 3.10 or later
|
|
390
|
+
- No mandatory dependencies
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Contributing
|
|
395
|
+
|
|
396
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## License
|
|
401
|
+
|
|
402
|
+
MIT — see [LICENSE](LICENSE).
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
<p align="center">
|
|
407
|
+
Built by <a href="https://github.com/sidx0">Siddhant B</a>
|
|
408
|
+
</p>
|