fastapi-route 0.1.0__py3-none-any.whl
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.
- fastapi_route-0.1.0.dist-info/METADATA +304 -0
- fastapi_route-0.1.0.dist-info/RECORD +58 -0
- fastapi_route-0.1.0.dist-info/WHEEL +5 -0
- fastapi_route-0.1.0.dist-info/entry_points.txt +2 -0
- fastapi_route-0.1.0.dist-info/licenses/LICENSE +1 -0
- fastapi_route-0.1.0.dist-info/top_level.txt +1 -0
- fastapi_router/__init__.py +34 -0
- fastapi_router/app.py +280 -0
- fastapi_router/build/__init__.py +5 -0
- fastapi_router/build/builder.py +211 -0
- fastapi_router/build/cache.py +352 -0
- fastapi_router/build/loader.py +93 -0
- fastapi_router/cli/commands.py +445 -0
- fastapi_router/config/loader.py +462 -0
- fastapi_router/config/py_loader.py +263 -0
- fastapi_router/config/schema.py +75 -0
- fastapi_router/config/validator.py +141 -0
- fastapi_router/constants.py +83 -0
- fastapi_router/core/builder.py +630 -0
- fastapi_router/core/lifecycle.py +89 -0
- fastapi_router/core/registry.py +147 -0
- fastapi_router/core/scanner.py +325 -0
- fastapi_router/core/validator.py +344 -0
- fastapi_router/custom/__init__.py +6 -0
- fastapi_router/custom/context.py +166 -0
- fastapi_router/custom/error_page.py +311 -0
- fastapi_router/custom/loader.py +269 -0
- fastapi_router/custom/validator.py +213 -0
- fastapi_router/decorators/__init__.py +5 -0
- fastapi_router/decorators/methods.py +130 -0
- fastapi_router/dev/config_watcher.py +136 -0
- fastapi_router/dev/error_handler.py +346 -0
- fastapi_router/dev/error_page.py +893 -0
- fastapi_router/dev/server.py +492 -0
- fastapi_router/docs/__init__.py +11 -0
- fastapi_router/docs/collector.py +270 -0
- fastapi_router/docs/generator.py +644 -0
- fastapi_router/docs/renderer.py +153 -0
- fastapi_router/exceptions.py +204 -0
- fastapi_router/middleware/custom.py +201 -0
- fastapi_router/middleware/manager.py +87 -0
- fastapi_router/middleware/validator.py +153 -0
- fastapi_router/request.py +211 -0
- fastapi_router/response.py +210 -0
- fastapi_router/routing/compiler.py +70 -0
- fastapi_router/routing/filesystem.py +67 -0
- fastapi_router/routing/matcher.py +89 -0
- fastapi_router/routing/parser.py +125 -0
- fastapi_router/routing/router.py +93 -0
- fastapi_router/static/__init__.py +7 -0
- fastapi_router/static/directory_listing.py +240 -0
- fastapi_router/static/handler.py +220 -0
- fastapi_router/static/middleware.py +115 -0
- fastapi_router/types.py +234 -0
- fastapi_router/utils/imports.py +70 -0
- fastapi_router/utils/logger.py +285 -0
- fastapi_router/utils/paths.py +67 -0
- fastapi_router/version.py +1 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-route
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: File-based routing for FastAPI with automatic route discovery
|
|
5
|
+
Author-email: Your Name <you@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: fastapi>=0.100.0
|
|
11
|
+
Requires-Dist: uvicorn>=0.23.0
|
|
12
|
+
Requires-Dist: pydantic>=2.0.0
|
|
13
|
+
Requires-Dist: watchdog>=3.0.0
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
16
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
17
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
18
|
+
Requires-Dist: httpx>=0.24.0; extra == "dev"
|
|
19
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
20
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
21
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
22
|
+
Requires-Dist: ruff>=0.0.260; extra == "dev"
|
|
23
|
+
Provides-Extra: test
|
|
24
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
25
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
|
|
27
|
+
Requires-Dist: httpx>=0.24.0; extra == "test"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
<p align="center">
|
|
31
|
+
<img src="https://raw.githubusercontent.com/fastapi-router/fastapi-router/main/docs/logo.png" alt="FastAPI Router Logo" width="300">
|
|
32
|
+
</p>
|
|
33
|
+
|
|
34
|
+
# FastAPI Router
|
|
35
|
+
|
|
36
|
+
A file-based routing system for FastAPI — build APIs with the simplicity of files and folders, not decorators.
|
|
37
|
+
|
|
38
|
+
**[Full Documentation](https://inject3r.github.io/fastapi-router)** — Complete API reference, advanced guides, and examples
|
|
39
|
+
|
|
40
|
+
<br/>
|
|
41
|
+
|
|
42
|
+
## Why FastAPI Router?
|
|
43
|
+
|
|
44
|
+
Stop writing decorators. Start organizing your API with files and folders. Just drop Python files in directories — each file becomes an endpoint, each directory becomes a route segment. Dynamic parameters? Use `[param]` directories. Route groups? Use `(group)` directories. It's that simple.
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- **File-Based Routing**: Routes defined by directory structure — no decorators needed
|
|
49
|
+
- **Dynamic Routes**: `[user_id]` directories automatically become `{user_id}` parameters
|
|
50
|
+
- **Cache-All Routes**: `[...slug]` captures unlimited path segments
|
|
51
|
+
- **Route Groups**: `(auth)` directories organize code without affecting URLs
|
|
52
|
+
- **Hot Reload**: Instant rebuild on file changes during development
|
|
53
|
+
- **Production Build Cache**: Pre-compiled routes for lightning-fast startup
|
|
54
|
+
- **Beautiful Error Pages**: Syntax highlighting, line numbers, and helpful suggestions
|
|
55
|
+
- **Custom Handlers**: Bring your own `docs.py` and `not-found.py`
|
|
56
|
+
- **Custom Middleware**: Intercept requests with `middleware.py`
|
|
57
|
+
- **Static File Serving**: Drop files in `/public` — served automatically
|
|
58
|
+
- **Built-in Documentation**: Auto-generated API docs at `/docs`
|
|
59
|
+
- **CLI Tools**: `init`, `build`, `run`, `dev`, `clean`, `status` commands
|
|
60
|
+
- **Zero Config**: Works out of the box — but fully customizable when you need it
|
|
61
|
+
- **Type Hints**: Full typing support for excellent IDE autocompletion
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install fastapi-router
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
With development dependencies:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install fastapi-router[dev]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
### 1. Initialize a new project
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
fastapi-router init
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. Create your first route
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# routes/index.py
|
|
87
|
+
from fastapi_router import Request
|
|
88
|
+
|
|
89
|
+
def GET(request: Request):
|
|
90
|
+
return {"message": "Hello World!"}
|
|
91
|
+
|
|
92
|
+
def POST(request: Request):
|
|
93
|
+
data = await request.json()
|
|
94
|
+
return {"received": data}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3. Add a dynamic route
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
# routes/users/[user_id]/route.py
|
|
101
|
+
from fastapi_router import Request
|
|
102
|
+
|
|
103
|
+
def GET(request: Request, user_id: int):
|
|
104
|
+
return {"user_id": user_id, "name": f"User {user_id}"}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 4. Run your API
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Development mode (with hot reload)
|
|
111
|
+
fastapi-router dev
|
|
112
|
+
|
|
113
|
+
# Production mode (requires build first)
|
|
114
|
+
fastapi-router build
|
|
115
|
+
fastapi-router run
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Visit `http://localhost:8000/docs` for auto-generated documentation.
|
|
119
|
+
|
|
120
|
+
## How It Works
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
routes/
|
|
124
|
+
├── index.py → GET /, POST /
|
|
125
|
+
├── about/
|
|
126
|
+
│ └── route.py → GET /about
|
|
127
|
+
├── users/
|
|
128
|
+
│ ├── route.py → GET /users, POST /users
|
|
129
|
+
│ ├── [user_id]/
|
|
130
|
+
│ │ └── route.py → GET /users/{user_id}, PUT /users/{user_id}
|
|
131
|
+
│ └── [user_id]/posts/
|
|
132
|
+
│ └── route.py → GET /users/{user_id}/posts
|
|
133
|
+
├── docs/
|
|
134
|
+
│ └── [...slug]/
|
|
135
|
+
│ └── route.py → GET /docs/*
|
|
136
|
+
└── (auth)/ # Route group (ignored in URL)
|
|
137
|
+
└── profile/
|
|
138
|
+
└── route.py → GET /profile
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## CLI Commands
|
|
142
|
+
|
|
143
|
+
| Command | Description |
|
|
144
|
+
| ----------------------- | ------------------------------------------- |
|
|
145
|
+
| `fastapi-router init` | Create a new project with default structure |
|
|
146
|
+
| `fastapi-router build` | Compile routes into production cache |
|
|
147
|
+
| `fastapi-router run` | Start production server (requires build) |
|
|
148
|
+
| `fastapi-router dev` | Start development server with hot reload |
|
|
149
|
+
| `fastapi-router clean` | Remove build cache |
|
|
150
|
+
| `fastapi-router status` | Show build cache information |
|
|
151
|
+
|
|
152
|
+
## Customization
|
|
153
|
+
|
|
154
|
+
### Configuration (`config.py`)
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
app_name = "My API"
|
|
158
|
+
debug = False
|
|
159
|
+
cors_enabled = True
|
|
160
|
+
cors_origins = ["https://example.com"]
|
|
161
|
+
|
|
162
|
+
server = {
|
|
163
|
+
"host": "0.0.0.0",
|
|
164
|
+
"port": 8080,
|
|
165
|
+
"workers": 4
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
logging = {
|
|
169
|
+
"level": "INFO",
|
|
170
|
+
"format": "[%Y-%m-%d %H:%M:%S]",
|
|
171
|
+
"color": True
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
commands = {
|
|
175
|
+
"deploy": "fastapi-router build && rsync -avz .cache/ deploy/"
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Custom Middleware (`middleware.py`)
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from fastapi_router import Request
|
|
183
|
+
|
|
184
|
+
async def middleware(request: Request, call_next):
|
|
185
|
+
# Log every request
|
|
186
|
+
print(f"{request.method} {request.path}")
|
|
187
|
+
|
|
188
|
+
# Continue to the route handler
|
|
189
|
+
response = await call_next(request)
|
|
190
|
+
|
|
191
|
+
# Add custom header
|
|
192
|
+
response.headers["X-Powered-By"] = "FastAPI Router"
|
|
193
|
+
|
|
194
|
+
return response
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Custom 404 Page (`not-found.py`)
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from fastapi_router import Request, HTMLResponse
|
|
201
|
+
|
|
202
|
+
def handler(request: Request, context):
|
|
203
|
+
routes = context.get_routes()
|
|
204
|
+
|
|
205
|
+
return HTMLResponse(content=f"""
|
|
206
|
+
<!DOCTYPE html>
|
|
207
|
+
<html>
|
|
208
|
+
<head><title>404 - Not Found</title></head>
|
|
209
|
+
<body>
|
|
210
|
+
<h1>404 - Page Not Found</h1>
|
|
211
|
+
<p>{request.path} does not exist</p>
|
|
212
|
+
<p>Total routes: {len(routes)}</p>
|
|
213
|
+
<a href="/">Go Home</a>
|
|
214
|
+
</body>
|
|
215
|
+
</html>
|
|
216
|
+
""", status_code=404)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Documentation
|
|
220
|
+
|
|
221
|
+
For complete documentation, API reference, and advanced usage examples, visit:
|
|
222
|
+
|
|
223
|
+
**[https://inject3r.github.io/fastapi-router](https://inject3r.github.io/fastapi-router)**
|
|
224
|
+
|
|
225
|
+
The documentation includes:
|
|
226
|
+
|
|
227
|
+
- Detailed API reference for all modules and classes
|
|
228
|
+
- Advanced routing patterns and best practices
|
|
229
|
+
- Configuration options and their effects
|
|
230
|
+
- Error handling strategies
|
|
231
|
+
- Migration guides from traditional FastAPI
|
|
232
|
+
- Production deployment guides
|
|
233
|
+
|
|
234
|
+
## Repository
|
|
235
|
+
|
|
236
|
+
Source code and issue tracking:
|
|
237
|
+
|
|
238
|
+
**[https://github.com/inject3r/fastapi-router](https://github.com/inject3r/fastapi-router)**
|
|
239
|
+
|
|
240
|
+
## Requirements
|
|
241
|
+
|
|
242
|
+
- Python 3.9+
|
|
243
|
+
- FastAPI 0.100.0+
|
|
244
|
+
- Uvicorn 0.23.0+
|
|
245
|
+
|
|
246
|
+
## Running Tests
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Run all tests with coverage
|
|
250
|
+
./scripts/test.sh
|
|
251
|
+
|
|
252
|
+
# Clean test output files
|
|
253
|
+
./scripts/test_clean.sh
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Project Structure
|
|
257
|
+
|
|
258
|
+
```text
|
|
259
|
+
your-project/
|
|
260
|
+
├── routes/ # Your API routes (file-based)
|
|
261
|
+
│ ├── index.py
|
|
262
|
+
│ └── ...
|
|
263
|
+
├── public/ # Static files (served at /)
|
|
264
|
+
│ ├── css/
|
|
265
|
+
│ ├── js/
|
|
266
|
+
│ └── images/
|
|
267
|
+
├── config.py # Application configuration
|
|
268
|
+
├── middleware.py # Custom middleware (optional)
|
|
269
|
+
├── docs.py # Custom documentation (optional)
|
|
270
|
+
├── not-found.py # Custom 404 page (optional)
|
|
271
|
+
└── .cache/ # Build cache (auto-generated)
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Why No Decorators?
|
|
275
|
+
|
|
276
|
+
FastAPI Router was built for developers who think in terms of files and directories, not decorators and route registrations. Just create files — they become endpoints. Create directories — they become route segments. Use `[param]` for dynamic parameters. Use `(group)` for organization.
|
|
277
|
+
|
|
278
|
+
No decorators. No route registration. Just files.
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
This project is licensed under the MIT License.
|
|
283
|
+
|
|
284
|
+
## Author
|
|
285
|
+
|
|
286
|
+
**Abolfazl Hosseini**
|
|
287
|
+
|
|
288
|
+
- Email: tryuzr@gmail.com
|
|
289
|
+
- GitHub: [@inject3r](https://github.com/inject3r)
|
|
290
|
+
|
|
291
|
+
## Contributing
|
|
292
|
+
|
|
293
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
294
|
+
|
|
295
|
+
1. Fork the repository
|
|
296
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
297
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
298
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
299
|
+
5. Open a Pull Request
|
|
300
|
+
|
|
301
|
+
## Acknowledgments
|
|
302
|
+
|
|
303
|
+
- [FastAPI](https://fastapi.tiangolo.com/) - The amazing web framework
|
|
304
|
+
- All contributors and users of this project
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
fastapi_route-0.1.0.dist-info/licenses/LICENSE,sha256=NAC7SVw_jEw0g6RMa8GpLp2UQG23Wm8n28zBHHZFDYo,5
|
|
2
|
+
fastapi_router/__init__.py,sha256=-PukPobOAT285s77hzDde9jWZDtt62VM56jXR_PIrMg,841
|
|
3
|
+
fastapi_router/app.py,sha256=3bJufHnLEUChReILbLWuuY-rXqN0VDAY7xsoUA3OY_I,10800
|
|
4
|
+
fastapi_router/constants.py,sha256=blF47Ep8zXzzz3E63fSP_tanCR9-aPVJA5GedNLcMLA,2879
|
|
5
|
+
fastapi_router/exceptions.py,sha256=3zcOXmvsex7LJewjhBdZjpfiAhnfTqlyyD5qTc_dUXw,7167
|
|
6
|
+
fastapi_router/request.py,sha256=gC1D2KmbN-qCNCGHva3T7wyzlW5nGflh80CAk8hd0-s,6927
|
|
7
|
+
fastapi_router/response.py,sha256=PUMKNgwSp90NFqT-e-BMlRDp-5R186X2IlVfHID_t2A,5985
|
|
8
|
+
fastapi_router/types.py,sha256=PlYAoGIsNfpqGiJxCDBJLsX1gPUUtGnHXx4Itbpm9Ys,7949
|
|
9
|
+
fastapi_router/version.py,sha256=Pru0BlFBASFCFo7McHdohtKkUtgMPDwbGfyUZlE2_Vw,21
|
|
10
|
+
fastapi_router/build/__init__.py,sha256=91l0wsho3C4oxb0DpZPja0icvsIm0DjfAwSHy1T2I7s,156
|
|
11
|
+
fastapi_router/build/builder.py,sha256=Ila_spVDLisULCivjBvR-eLRwIr8qscmMqJEXf0OCXo,9355
|
|
12
|
+
fastapi_router/build/cache.py,sha256=eu2qV6EDC1hJECMArqD_OE17u_UMJiqRlGw56jhh_K4,13100
|
|
13
|
+
fastapi_router/build/loader.py,sha256=m6TANs0QHmpwXH70uY8dLmm_TsCl457eAkDP920h6eQ,3103
|
|
14
|
+
fastapi_router/cli/commands.py,sha256=vZRGrkjjDcp3p4gvPbFSsaPiB6D8EE9sCKzAiwU_bDE,14443
|
|
15
|
+
fastapi_router/config/loader.py,sha256=yUfjKp3m-4D7PtFqKSZD0YZ9VPS92JDYeb_AQgNQx-I,15656
|
|
16
|
+
fastapi_router/config/py_loader.py,sha256=dG_YVf01f-btrEIS26wfc4ndW81KAePry0Y73rgAwhQ,9519
|
|
17
|
+
fastapi_router/config/schema.py,sha256=CGIJZ9LwBNvu7RdKm8v1_2mZMWIXwP4Mulr_YQtIevw,2885
|
|
18
|
+
fastapi_router/config/validator.py,sha256=8od8V3L8duRUqrEiVoMeLZIba8QoYEofECefCmN10MI,6301
|
|
19
|
+
fastapi_router/core/builder.py,sha256=0dI4XpUFsZ10sGsd1RjL52aI_knmbC_CSZwq4SYgIbA,28054
|
|
20
|
+
fastapi_router/core/lifecycle.py,sha256=M7V4p7rVAhPU67qWhRyvqWYkAhgYkjdDod5cW49g81o,3303
|
|
21
|
+
fastapi_router/core/registry.py,sha256=X0nEgqBnfF3p9OCjEWVLy7_MFcpaCetcavtuf1WAx64,4991
|
|
22
|
+
fastapi_router/core/scanner.py,sha256=FCPXZODXCzXbps4hzzUCmjSxufo9VKevv61TlAdRdRc,12324
|
|
23
|
+
fastapi_router/core/validator.py,sha256=mX8mEcGKBK92-8zNy2I9EA-9IJvw3R8zwmrqk_Sv3qM,12899
|
|
24
|
+
fastapi_router/custom/__init__.py,sha256=YfyZWKvceo5u5tpzC5ouNdUJL4tOb__gXhmezHxFqr0,186
|
|
25
|
+
fastapi_router/custom/context.py,sha256=kJxjrCHk5VFVbM3iTTY00UsyW9dAIUctrf1YQpaxTaY,5666
|
|
26
|
+
fastapi_router/custom/error_page.py,sha256=BIq4hGktQv1DX3I6o-7XO6ckcuKVNhWa84AKkIA48U4,8511
|
|
27
|
+
fastapi_router/custom/loader.py,sha256=t42QCEObG_qBJZdVNc8bdZ6-x7inlaj0-BumUWgBhJA,10723
|
|
28
|
+
fastapi_router/custom/validator.py,sha256=y_zeV3JUROjUfAHqzuhN6Q4ouBcm23kJDzcmpg9M5eU,8794
|
|
29
|
+
fastapi_router/decorators/__init__.py,sha256=LGfYrvPdU76S6dmahJm-bm5ZhWITMUTudg0Io0j-ywE,142
|
|
30
|
+
fastapi_router/decorators/methods.py,sha256=MH60kMKXNTMcxCEoHf4PBIlU0wjkNPNcINoC-tbHrKQ,3483
|
|
31
|
+
fastapi_router/dev/config_watcher.py,sha256=9mIkf83jQ3rz2P2tUkJPA2rtt4l5qh1n47lEU5YZWTg,4797
|
|
32
|
+
fastapi_router/dev/error_handler.py,sha256=yGmJdVT8zIBQTqYulZiFpO707wuoayhKilTaomku2uk,10950
|
|
33
|
+
fastapi_router/dev/error_page.py,sha256=sXkduQJHzJSO_384cn2MfPwpWJ_f0iYY_GwwykXf6ec,27384
|
|
34
|
+
fastapi_router/dev/server.py,sha256=NT5IsYD3LP2HU0NoQulKMozz7GCOj1xJlpZO99e46X4,19707
|
|
35
|
+
fastapi_router/docs/__init__.py,sha256=MX-gTxmOwh9nAsleVrcNe_gc1YY_-5r0eVe0ZiL1SqI,233
|
|
36
|
+
fastapi_router/docs/collector.py,sha256=CWiE6R-Ew8ZlmmJribXmtC1e42uX544K8BbAwi_bOXY,8882
|
|
37
|
+
fastapi_router/docs/generator.py,sha256=mvqsidetpKGWSqRVhGQzLllfx9K5Kc6swbqPOle2JGM,20772
|
|
38
|
+
fastapi_router/docs/renderer.py,sha256=i3jdo0GQr26SeKfg99Z_5CD-hGrddfT-Qc1iAOY-Mbs,5628
|
|
39
|
+
fastapi_router/middleware/custom.py,sha256=fReInKnLCsCb1RC3jR7jYkP_WjPZFKWY_rPpPgW2YWM,7670
|
|
40
|
+
fastapi_router/middleware/manager.py,sha256=GRY5XDHssDeZ1I9z7ahmCxpX12u42OKY9DGWQ32uxwA,3403
|
|
41
|
+
fastapi_router/middleware/validator.py,sha256=xc38xzvdUptDJS_wPMDZr42QYSI8nFoiq1Laj_6H-cc,6426
|
|
42
|
+
fastapi_router/routing/compiler.py,sha256=iYBTM5ISmoYiMRgSiUiUluiCg91NVvwccHbLD_hWPMk,2588
|
|
43
|
+
fastapi_router/routing/filesystem.py,sha256=MaCS6YlyG8Kif-hupK2WxS5Mwcexhb5zktR15qLFtxM,2141
|
|
44
|
+
fastapi_router/routing/matcher.py,sha256=_ARdCy7vFK28xDAqzYRlqetUXIm62PDFW8_QuUxIsEw,3179
|
|
45
|
+
fastapi_router/routing/parser.py,sha256=ZtXBY_qgqS7jbzad7pM8bWy3MT3HwUaa8-T_4jkSQSw,4623
|
|
46
|
+
fastapi_router/routing/router.py,sha256=yY_Jnx0n6B1wWYReKH5sQLNVSFvW_0cJnB8_H5mffQo,3122
|
|
47
|
+
fastapi_router/static/__init__.py,sha256=kAiHJFMrgBfzYZezazcz-gB0PncZAUGUpxIFxnkS6rQ,242
|
|
48
|
+
fastapi_router/static/directory_listing.py,sha256=c8VoT4_2bdlhOCpolBbVY8IbCd5XtvE9hZT6mq6BrbA,6751
|
|
49
|
+
fastapi_router/static/handler.py,sha256=wW_o8CWrRSTuNoWymvehYgBlZJk5R6HA0j0w156eLbo,8219
|
|
50
|
+
fastapi_router/static/middleware.py,sha256=i-RIB1ugXlCaaqSEnjxpG59EqLwacW9LJQova7ZGutA,4536
|
|
51
|
+
fastapi_router/utils/imports.py,sha256=HK6mSaHxcncLsY42tfyKaKVCq92jW41uar1X2LsqcEU,2387
|
|
52
|
+
fastapi_router/utils/logger.py,sha256=Fb6P2y6bWxKluiwWBAzcP1RNZwb_2hXtRlREjq6EjUI,9253
|
|
53
|
+
fastapi_router/utils/paths.py,sha256=3KnHY7AV6GH3flDkkTYBKpD6BkJ_F0rvE1j-AEH_kT4,1977
|
|
54
|
+
fastapi_route-0.1.0.dist-info/METADATA,sha256=lgcSOE2q7FLM9V35qHrS-NIeQDFk_2XqudD-cE2FMhU,8847
|
|
55
|
+
fastapi_route-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
56
|
+
fastapi_route-0.1.0.dist-info/entry_points.txt,sha256=uas8lkNWaE-YcKwF7CS3j27mXu5c4U27iiIy5DB3aOI,68
|
|
57
|
+
fastapi_route-0.1.0.dist-info/top_level.txt,sha256=hZIWNXAsKb6zyMweL16KPzeKS7XqBsrH6AGxS7ZT0I4,15
|
|
58
|
+
fastapi_route-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lorem
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fastapi_router
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""FastAPI Router - File-based routing for FastAPI"""
|
|
2
|
+
|
|
3
|
+
from .app import create_app, FastAPIRouterApp
|
|
4
|
+
from .version import __version__
|
|
5
|
+
from .routing.router import Router
|
|
6
|
+
from .request import Request
|
|
7
|
+
from .response import Response, JSONResponse, HTMLResponse, PlainTextResponse, RedirectResponse
|
|
8
|
+
from .exceptions import (
|
|
9
|
+
HTTPException,
|
|
10
|
+
BadRequestException,
|
|
11
|
+
UnauthorizedException,
|
|
12
|
+
ForbiddenException,
|
|
13
|
+
NotFoundException,
|
|
14
|
+
MethodNotAllowedException,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"create_app",
|
|
19
|
+
"FastAPIRouterApp",
|
|
20
|
+
"__version__",
|
|
21
|
+
"Router",
|
|
22
|
+
"Request",
|
|
23
|
+
"Response",
|
|
24
|
+
"JSONResponse",
|
|
25
|
+
"HTMLResponse",
|
|
26
|
+
"PlainTextResponse",
|
|
27
|
+
"RedirectResponse",
|
|
28
|
+
"HTTPException",
|
|
29
|
+
"BadRequestException",
|
|
30
|
+
"UnauthorizedException",
|
|
31
|
+
"ForbiddenException",
|
|
32
|
+
"NotFoundException",
|
|
33
|
+
"MethodNotAllowedException",
|
|
34
|
+
]
|
fastapi_router/app.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main application factory for FastAPI Router.
|
|
3
|
+
|
|
4
|
+
This module provides the core application class that builds and runs the
|
|
5
|
+
FastAPI application. It handles route discovery, caching, configuration,
|
|
6
|
+
and lifecycle management.
|
|
7
|
+
|
|
8
|
+
The application can run in two modes:
|
|
9
|
+
- Development: Routes are scanned from filesystem on each build
|
|
10
|
+
- Production: Routes are loaded from build cache for faster startup
|
|
11
|
+
|
|
12
|
+
Features:
|
|
13
|
+
- Automatic route discovery from filesystem
|
|
14
|
+
- Build cache support for production
|
|
15
|
+
- Custom documentation endpoint
|
|
16
|
+
- Lifecycle hooks for startup/shutdown
|
|
17
|
+
- Configurable logging levels
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Optional, Any, Dict, List
|
|
23
|
+
|
|
24
|
+
from .config.loader import ConfigLoader
|
|
25
|
+
from .core.registry import RouteRegistry
|
|
26
|
+
from .core.builder import AppBuilder
|
|
27
|
+
from .core.lifecycle import LifecycleManager
|
|
28
|
+
from .routing.filesystem import FileSystemRouter
|
|
29
|
+
from .build import CacheLoader
|
|
30
|
+
from .types import RouteInfo
|
|
31
|
+
from .utils.logger import logger, Logger
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FastAPIRouterApp:
|
|
35
|
+
"""
|
|
36
|
+
Main application class for FastAPI Router.
|
|
37
|
+
|
|
38
|
+
This class orchestrates the entire application lifecycle:
|
|
39
|
+
1. Load configuration
|
|
40
|
+
2. Discover routes (from filesystem or cache)
|
|
41
|
+
3. Build the FastAPI application
|
|
42
|
+
4. Set up middleware and handlers
|
|
43
|
+
5. Run the server
|
|
44
|
+
|
|
45
|
+
The application can be used in two ways:
|
|
46
|
+
- Direct execution: app = FastAPIRouterApp(); app.run()
|
|
47
|
+
- As ASGI app: app = FastAPIRouterApp(); app = app.build()
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
config_path: Optional path to configuration file
|
|
51
|
+
enable_docs: Whether to enable the /docs endpoint
|
|
52
|
+
custom_docs_template: Custom HTML template for documentation
|
|
53
|
+
use_cache: Use build cache for routes (production mode)
|
|
54
|
+
is_production: Run in production mode (reduced logging)
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, config_path: Optional[str] = None, enable_docs: bool = True,
|
|
58
|
+
custom_docs_template: Optional[str] = None, use_cache: bool = False,
|
|
59
|
+
is_production: bool = False):
|
|
60
|
+
self.config_path = config_path
|
|
61
|
+
self.config = ConfigLoader.load(config_path)
|
|
62
|
+
|
|
63
|
+
# Apply settings from config or constructor args
|
|
64
|
+
self.enable_docs = enable_docs if hasattr(self.config, 'docs_enabled') else enable_docs
|
|
65
|
+
self.custom_docs_template = custom_docs_template or getattr(self.config, 'custom_docs_template', None)
|
|
66
|
+
self.use_cache = use_cache
|
|
67
|
+
self.is_production = is_production
|
|
68
|
+
|
|
69
|
+
# Configure logger based on mode
|
|
70
|
+
Logger.set_production_mode(is_production)
|
|
71
|
+
|
|
72
|
+
# Initialize core components
|
|
73
|
+
self.registry = RouteRegistry()
|
|
74
|
+
self.lifecycle = LifecycleManager()
|
|
75
|
+
self._fastapi_app = None
|
|
76
|
+
self._is_built = False
|
|
77
|
+
|
|
78
|
+
def _load_handler_from_file(self, file_path: Path, handler_name: str):
|
|
79
|
+
"""
|
|
80
|
+
Dynamically load a handler function from a Python file.
|
|
81
|
+
|
|
82
|
+
This is used in production mode to load route handlers from the
|
|
83
|
+
original source files when using build cache.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
file_path: Path to the route file
|
|
87
|
+
handler_name: Name of the handler function (e.g., "GET")
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Handler function if found, None otherwise
|
|
91
|
+
"""
|
|
92
|
+
import importlib.util
|
|
93
|
+
import sys
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
# Convert file path to module name
|
|
97
|
+
module_name = str(file_path).replace("/", ".").replace("\\", ".").replace(".py", "")
|
|
98
|
+
|
|
99
|
+
# Use cached module if available
|
|
100
|
+
if module_name in sys.modules:
|
|
101
|
+
module = sys.modules[module_name]
|
|
102
|
+
else:
|
|
103
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
104
|
+
if spec is None or spec.loader is None:
|
|
105
|
+
return None
|
|
106
|
+
module = importlib.util.module_from_spec(spec)
|
|
107
|
+
sys.modules[module_name] = module
|
|
108
|
+
spec.loader.exec_module(module)
|
|
109
|
+
|
|
110
|
+
return getattr(module, handler_name, None)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
if not self.is_production:
|
|
113
|
+
logger.error(f"Failed to load handler {handler_name} from {file_path}: {e}")
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
def build(self) -> Any:
|
|
117
|
+
"""
|
|
118
|
+
Build and return the FastAPI application.
|
|
119
|
+
|
|
120
|
+
This method performs the actual application construction:
|
|
121
|
+
1. Discover routes (filesystem or cache)
|
|
122
|
+
2. Register routes in the registry
|
|
123
|
+
3. Build the FastAPI app with all middleware and handlers
|
|
124
|
+
4. Set up lifecycle events
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Configured FastAPI application instance
|
|
128
|
+
"""
|
|
129
|
+
if self._is_built and self._fastapi_app:
|
|
130
|
+
return self._fastapi_app
|
|
131
|
+
|
|
132
|
+
if not self.is_production:
|
|
133
|
+
logger.info("Building FastAPI Router application...")
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
routes: List[RouteInfo] = []
|
|
137
|
+
routes_metadata: List[Dict[str, Any]] = []
|
|
138
|
+
|
|
139
|
+
# Try to load from cache if production mode
|
|
140
|
+
if self.use_cache:
|
|
141
|
+
cache_loader = CacheLoader(Path.cwd())
|
|
142
|
+
routes_metadata = cache_loader.load_routes_metadata()
|
|
143
|
+
if routes_metadata:
|
|
144
|
+
if not self.is_production:
|
|
145
|
+
logger.info(f"Loading {len(routes_metadata)} routes from cache")
|
|
146
|
+
else:
|
|
147
|
+
if not self.is_production:
|
|
148
|
+
logger.warning("Cache invalid or missing, falling back to filesystem scan")
|
|
149
|
+
self.use_cache = False
|
|
150
|
+
|
|
151
|
+
# Fall back to filesystem scan if cache not available
|
|
152
|
+
if not self.use_cache:
|
|
153
|
+
router = FileSystemRouter(self.config.route_dir)
|
|
154
|
+
routes = router.scan()
|
|
155
|
+
if not self.is_production:
|
|
156
|
+
logger.info(f"Discovered {len(routes)} routes via filesystem scan")
|
|
157
|
+
else:
|
|
158
|
+
# Load handlers from source files for cached routes
|
|
159
|
+
for meta in routes_metadata:
|
|
160
|
+
handler = self._load_handler_from_file(
|
|
161
|
+
Path(meta['file_path']),
|
|
162
|
+
meta['method']
|
|
163
|
+
)
|
|
164
|
+
if handler is None:
|
|
165
|
+
if not self.is_production:
|
|
166
|
+
logger.error(f"Failed to load handler for {meta['method']} {meta['path']}")
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
route = RouteInfo(
|
|
170
|
+
path=meta['path'],
|
|
171
|
+
method=meta['method'],
|
|
172
|
+
handler=handler,
|
|
173
|
+
file_path=Path(meta['file_path']),
|
|
174
|
+
is_dynamic=meta['is_dynamic'],
|
|
175
|
+
param_names=meta['param_names']
|
|
176
|
+
)
|
|
177
|
+
routes.append(route)
|
|
178
|
+
|
|
179
|
+
if not self.is_production:
|
|
180
|
+
logger.info(f"Loaded {len(routes)} routes from cache")
|
|
181
|
+
|
|
182
|
+
# No routes found - cannot start
|
|
183
|
+
if not routes:
|
|
184
|
+
logger.warning("No routes found")
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
# Register all routes
|
|
188
|
+
for route in routes:
|
|
189
|
+
self.registry.register(route)
|
|
190
|
+
|
|
191
|
+
# Build the FastAPI application
|
|
192
|
+
builder = AppBuilder(self.config, self.registry, self.enable_docs, self.custom_docs_template)
|
|
193
|
+
self._fastapi_app = builder.build()
|
|
194
|
+
|
|
195
|
+
# Set up lifecycle hooks
|
|
196
|
+
self.lifecycle.setup(self._fastapi_app)
|
|
197
|
+
|
|
198
|
+
self._is_built = True
|
|
199
|
+
|
|
200
|
+
# Log success
|
|
201
|
+
if not self.is_production:
|
|
202
|
+
logger.info(f"Application built with {len(routes)} routes")
|
|
203
|
+
if self.enable_docs:
|
|
204
|
+
logger.info(f"Documentation available at /docs")
|
|
205
|
+
|
|
206
|
+
return self._fastapi_app
|
|
207
|
+
|
|
208
|
+
except Exception as e:
|
|
209
|
+
logger.error(f"Build failed: {e}")
|
|
210
|
+
if not self.is_production:
|
|
211
|
+
import traceback
|
|
212
|
+
traceback.print_exc()
|
|
213
|
+
raise
|
|
214
|
+
|
|
215
|
+
def run(self, host: str = "127.0.0.1", port: int = 8000, reload: bool = False):
|
|
216
|
+
"""
|
|
217
|
+
Run the application using uvicorn server.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
host: Host address to bind to
|
|
221
|
+
port: Port number to listen on
|
|
222
|
+
reload: Enable auto-reload (development mode)
|
|
223
|
+
"""
|
|
224
|
+
try:
|
|
225
|
+
import uvicorn
|
|
226
|
+
except ImportError:
|
|
227
|
+
logger.error("uvicorn not installed. Please install it: pip install uvicorn")
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
app = self.build()
|
|
231
|
+
|
|
232
|
+
if app is None:
|
|
233
|
+
logger.error("No routes found, cannot start server")
|
|
234
|
+
return
|
|
235
|
+
|
|
236
|
+
mode = "development" if reload else "production"
|
|
237
|
+
logger.info(f"Starting {mode} server on http://{host}:{port}")
|
|
238
|
+
|
|
239
|
+
if not reload and self.use_cache:
|
|
240
|
+
logger.info("Running in production mode with build cache")
|
|
241
|
+
|
|
242
|
+
# Reduce uvicorn log noise in production
|
|
243
|
+
if self.is_production:
|
|
244
|
+
import logging
|
|
245
|
+
log_config = uvicorn.config.LOGGING_CONFIG
|
|
246
|
+
log_config["loggers"]["uvicorn"]["level"] = "WARNING"
|
|
247
|
+
log_config["loggers"]["uvicorn.access"]["level"] = "WARNING"
|
|
248
|
+
log_config["loggers"]["uvicorn.error"]["level"] = "WARNING"
|
|
249
|
+
uvicorn.run(app, host=host, port=port, reload=reload,
|
|
250
|
+
log_config=log_config, access_log=False)
|
|
251
|
+
else:
|
|
252
|
+
uvicorn.run(app, host=host, port=port, reload=reload)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def create_app(config_path: Optional[str] = None, enable_docs: bool = True,
|
|
256
|
+
custom_docs_template: Optional[str] = None, use_cache: bool = False,
|
|
257
|
+
is_production: bool = False) -> Any:
|
|
258
|
+
"""
|
|
259
|
+
Factory function to create a FastAPI Router application.
|
|
260
|
+
|
|
261
|
+
This is a convenience wrapper around FastAPIRouterApp for use with
|
|
262
|
+
ASGI servers like uvicorn directly.
|
|
263
|
+
|
|
264
|
+
Example:
|
|
265
|
+
app = create_app()
|
|
266
|
+
# Use with uvicorn: uvicorn.run(app)
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
config_path: Optional path to configuration file
|
|
270
|
+
enable_docs: Enable documentation endpoint
|
|
271
|
+
custom_docs_template: Custom HTML template for docs
|
|
272
|
+
use_cache: Use build cache (production mode)
|
|
273
|
+
is_production: Run in production mode
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Configured FastAPI application instance
|
|
277
|
+
"""
|
|
278
|
+
app_wrapper = FastAPIRouterApp(config_path, enable_docs, custom_docs_template,
|
|
279
|
+
use_cache, is_production)
|
|
280
|
+
return app_wrapper.build()
|