(Feat): Initial Commit.

This commit is contained in:
2026-01-05 22:11:58 +00:00
commit b9224dd031
21 changed files with 3259 additions and 0 deletions

359
README.md Normal file
View File

@@ -0,0 +1,359 @@
# FastAPI Route Loader
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
Automatic APIRouter loading and management for FastAPI with an event dispatch system.
## Features
- 🚀 **Automatic Router Discovery**: Automatically load APIRouter instances from modules and directories
- 🎯 **Router Management**: Include/exclude routers dynamically with filtering capabilities
- 📡 **Event System**: Subscribe to router lifecycle events (loaded, unloaded, updated)
- 🔧 **Type Safe**: Full type hints with strict pyright and ruff compliance
-**Production Ready**: Comprehensive test coverage and battle-tested code
- 🐍 **Python 3.10+**: Modern Python with full async support
## Installation
```bash
pip install fastapi-route-loader
```
## Quick Start
### Basic Usage
```python
from fastapi import FastAPI, APIRouter
from fastapi_route_loader import RouterContainer
# Create a container
container = RouterContainer()
# Add routers manually
users_router = APIRouter(prefix="/users", tags=["users"])
@users_router.get("/")
def list_users():
return {"users": []}
container.add_router("users", users_router)
# Register all routers to your FastAPI app
app = FastAPI()
container.register_to_app(app)
```
### Automatic Loading from Directory
```python
from fastapi import FastAPI
from fastapi_route_loader import RouterContainer
# Create a container and load all routers from a directory
container = RouterContainer()
container.load_from_directory("./routers", package="myapp.routers")
# Register to FastAPI app
app = FastAPI()
container.register_to_app(app, prefix="/api")
```
### Router Filtering
```python
from fastapi_route_loader import RouterContainer
container = RouterContainer()
container.load_from_directory("./routers")
# Exclude specific routers
container.exclude("admin_router", "internal_router")
# Or use whitelist mode (only include specific routers)
container.include("public_router", "api_router")
# Clear all filters
container.clear_filters()
```
### Event System
The event system supports both decorator and callback styles:
**Decorator Style (Recommended):**
```python
from fastapi_route_loader import RouterContainer, RouterEventType
container = RouterContainer()
# Subscribe to specific event types using decorators
@container.on(RouterEventType.LOADED)
def on_router_loaded(event):
print(f"Router '{event.router_name}' was loaded")
print(f"Metadata: {event.metadata}")
# Subscribe to all events
@container.on(None)
def log_all_events(event):
print(f"Event: {event.event_type.value} - Router: {event.router_name}")
# Load routers (events will be dispatched)
container.load_from_directory("./routers")
```
**Callback Style:**
```python
def on_router_loaded(event):
print(f"Router '{event.router_name}' was loaded")
container.on(RouterEventType.LOADED, on_router_loaded)
```
## Advanced Usage
### Project Structure
```
myapp/
├── main.py
└── routers/
├── __init__.py
├── users.py
├── posts.py
└── admin/
├── __init__.py
└── dashboard.py
```
**routers/users.py:**
```python
from fastapi import APIRouter
users_router = APIRouter(prefix="/users", tags=["users"])
@users_router.get("/")
def list_users():
return {"users": []}
@users_router.get("/{user_id}")
def get_user(user_id: int):
return {"user_id": user_id}
```
**main.py:**
```python
from fastapi import FastAPI
from fastapi_route_loader import RouterContainer
app = FastAPI()
container = RouterContainer()
# Load all routers from the routers directory
container.load_from_directory("./routers", package="myapp.routers")
# Register all active routers
container.register_to_app(app)
```
### Dynamic Router Management
```python
from fastapi import APIRouter
from fastapi_route_loader import RouterContainer
container = RouterContainer()
# Add a router
router_v1 = APIRouter(prefix="/api/v1")
container.add_router("api_v1", router_v1)
# Update a router
router_v2 = APIRouter(prefix="/api/v2")
container.update_router("api_v1", router_v2)
# Remove a router
container.remove_router("api_v1")
# Check if router exists
if container.has_router("api_v1"):
router = container.get_router("api_v1")
```
### Event Handling with Metadata
```python
from fastapi_route_loader import RouterContainer, RouterEventType
container = RouterContainer()
def track_router_changes(event):
if event.event_type == RouterEventType.LOADED:
print(f"Loaded: {event.router_name}")
elif event.event_type == RouterEventType.UPDATED:
print(f"Updated: {event.router_name}")
print(f"Old router: {event.old_router}")
elif event.event_type == RouterEventType.UNLOADED:
print(f"Unloaded: {event.router_name}")
container.on(None, track_router_changes)
# Add router with metadata
from fastapi import APIRouter
router = APIRouter()
container.add_router("my_router", router, metadata={"version": "1.0", "author": "dev"})
```
### Container Iteration
```python
container = RouterContainer()
# ... add routers ...
# Check length
print(f"Total routers: {len(container)}")
# Check membership
if "users_router" in container:
print("Users router exists")
# Iterate over router names
for router_name in container:
print(f"Router: {router_name}")
# Get all routers (including filtered ones)
all_routers = container.get_all_routers()
# Get only active routers (after applying filters)
active_routers = container.get_active_routers()
```
## API Reference
### RouterContainer
Main class for managing APIRouter instances.
#### Methods
- `add_router(name: str, router: APIRouter, metadata: dict | None = None) -> None`
- Add a router to the container
- `remove_router(name: str, metadata: dict | None = None) -> APIRouter`
- Remove and return a router from the container
- `update_router(name: str, router: APIRouter, metadata: dict | None = None) -> None`
- Update an existing router
- `get_router(name: str) -> APIRouter`
- Get a router by name
- `has_router(name: str) -> bool`
- Check if a router exists
- `exclude(*names: str) -> None`
- Exclude routers from being active
- `include(*names: str) -> None`
- Set routers to be included (whitelist mode)
- `clear_filters() -> None`
- Clear all include/exclude filters
- `is_active(name: str) -> bool`
- Check if a router is active
- `get_active_routers() -> dict[str, APIRouter]`
- Get all active routers
- `get_all_routers() -> dict[str, APIRouter]`
- Get all routers regardless of filters
- `load_from_module(module_path: str) -> None`
- Load routers from a module
- `load_from_directory(directory: str, package: str | None = None) -> None`
- Load routers from a directory
- `register_to_app(app: FastAPI, prefix: str = "") -> None`
- Register all active routers to a FastAPI application
- `on(event_type: RouterEventType | None, handler: EventHandler) -> None`
- Subscribe to router events
- `off(event_type: RouterEventType | None, handler: EventHandler) -> None`
- Unsubscribe from router events
- `clear_handlers() -> None`
- Clear all event handlers
### RouterEventType
Enum of event types:
- `LOADED`: Router was loaded
- `UNLOADED`: Router was unloaded
- `UPDATED`: Router was updated
### Event Classes
- `RouterLoadedEvent`: Dispatched when a router is loaded
- `RouterUnloadedEvent`: Dispatched when a router is unloaded
- `RouterUpdatedEvent`: Dispatched when a router is updated (includes `old_router` attribute)
## Development
### Setup
```bash
# Clone the repository
git clone https://github.com/yourusername/fastapi-route-loader.git
cd fastapi-route-loader
# Install dependencies
pip install -e ".[dev]"
```
### Running Tests
```bash
# Run all tests with coverage
pytest
# Run with verbose output
pytest -v
# Run specific test file
pytest tests/test_container.py
```
### Code Quality
```bash
# Run ruff linter
ruff check .
# Run ruff formatter
ruff format .
# Run pyright type checker
pyright
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the MIT License - see the LICENSE file for details.
## Changelog
### 0.1.0 (Initial Release)
- Automatic router discovery and loading
- Router filtering (include/exclude)
- Event dispatch system
- Full type hints and strict linting
- Comprehensive test coverage