A FastAPI-style dependency injection system for aiogram Telegram bots. This library brings clean, type-safe dependency injection to your aiogram handlers, making your code more modular, testable, and maintainable.
Project description
Aiogram Dependency Injection
A FastAPI-style dependency injection system for aiogram Telegram bots. This library brings clean, type-safe dependency injection to your aiogram handlers, making your code more modular, testable, and maintainable.
Features
- 🚀 FastAPI-style syntax - Familiar
Depends()decorator - 🔄 Multiple dependency scopes - Singleton, Request, and Transient
- 🏗️ Nested dependencies - Dependencies can depend on other dependencies
- ⚡ Async support - Both sync and async dependency functions
- 🛡️ Circular dependency detection - Prevents infinite loops
- 🧪 Type-safe - Full type hints support
- 💾 Smart caching - Efficient resource management
Installation
pip install aiogram-dependency
Or install from source:
git clone https://github.com/AstralMortem/aiogram-dependency
cd aiogram-dependency
pip install -e .
Quick Start
import asyncio
from aiogram import Bot, Dispatcher, F
from aiogram.types import Message
from aiogram_dependency import Depends, DependencyMiddleware, Scope
# Define your dependencies
class DatabaseConnection:
def __init__(self, connection_string: str):
self.connection_string = connection_string
async def query(self, sql: str):
# Your database logic here
return f"Result for: {sql}"
class UserService:
def __init__(self, db: DatabaseConnection):
self.db = db
async def get_user_profile(self, user_id: int):
return await self.db.query(f"SELECT * FROM users WHERE id = {user_id}")
# Dependency factories
async def get_database() -> DatabaseConnection:
return DatabaseConnection("postgresql://localhost/mydb")
async def get_user_service(
db: DatabaseConnection = Depends(get_database, scope=Scope.SINGLETON)
) -> UserService:
return UserService(db)
# Handler with dependency injection
async def profile_handler(
message: Message,
user_service: UserService = Depends(get_user_service)
):
if not message.from_user:
await message.answer("User not found")
return
profile = await user_service.get_user_profile(message.from_user.id)
await message.answer(f"Your profile: {profile}")
# Setup bot
async def main():
bot = Bot(token="YOUR_BOT_TOKEN")
dp = Dispatcher()
# Register dependency injection middleware
dp.message.middleware(DependencyMiddleware())
# Register handlers
dp.message.register(profile_handler, F.text == "/profile")
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Dependency Scopes
Singleton
Created once and shared across all requests globally.
async def get_database() -> DatabaseConnection:
return DatabaseConnection("connection_string")
async def handler(
message: Message,
db: DatabaseConnection = Depends(get_database, scope=DependencyScope.SINGLETON)
):
# Same database instance for all users
pass
Request (Default)
Created once per user/chat and cached for subsequent calls in the same context.
async def get_user_service() -> UserService:
return UserService()
async def handler(
message: Message,
service: UserService = Depends(get_user_service, scope=DependencyScope.REQUEST)
):
# Same service instance for this user, different for other users
pass
Transient
Created fresh every time it's requested.
async def get_timestamp() -> float:
return time.time()
async def handler(
message: Message,
timestamp: float = Depends(get_timestamp, scope=DependencyScope.TRANSIENT)
):
# New timestamp every time
pass
Advanced Usage
Annotated
You can use Annotated type to make it more readable.
from typing import Annotated
from aiogram_dependency import Depends
from aiogram import Dispatcher,filters,types
async def get_database() -> DatabaseConnection:
return DatabaseConnection("postgresql://localhost/db")
DatabaseDep = Annotated[DatabaseConnection, Depends(get_database)]
async def get_user(db: DatabaseDep):
return db.execute('SQL')
UserDep = Annotated[dict, Depends(get_user)]
dp = Dispatcher()
@dp.message(filters.CommandStart())
async def start(message:types.Message, user: UserDep):
return await message.answer(user['username'])
Nested Dependencies
Dependencies can depend on other dependencies:
async def get_database() -> DatabaseConnection:
return DatabaseConnection("postgresql://localhost/db")
async def get_user_repository(
db: DatabaseConnection = Depends(get_database)
) -> UserRepository:
return UserRepository(db)
async def get_user_service(
user_repo: UserRepository = Depends(get_user_repository),
notification_service: NotificationService = Depends(get_notification_service)
) -> UserService:
return UserService(user_repo, notification_service)
Using Event Data in Dependencies
Dependencies can access the current event and handler data:
async def get_current_user(event: Message) -> Optional[User]:
return event.from_user
async def get_user_permissions(
event: Message,
data: dict,
current_user: User = Depends(get_current_user)
) -> List[str]:
# Access event, data, and other dependencies
if current_user and current_user.id in data.get('admins', []):
return ['admin', 'user']
return ['user']
async def admin_handler(
message: Message,
permissions: List[str] = Depends(get_user_permissions)
):
if 'admin' not in permissions:
await message.answer("Access denied")
return
await message.answer("Welcome, admin!")
Custom Registry and Resolver
For advanced use cases, you can customize the dependency system:
from aiogram_dependency.registry import DependencyRegistry
from aiogram_dependency.resolver import DependencyResolver
from aiogram_dependency import DependencyMiddleware
# Create custom registry
registry = DependencyRegistry()
# Pre-populate with some dependencies
registry.set_dependency(
get_database,
DatabaseConnection("custom://connection"),
DependencyScope.SINGLETON,
"global"
)
# Use custom middleware
middleware = DependencyMiddleware(registry)
dp.message.middleware(middleware)
Testing
The library is fully testable. Here's an example:
Run the full test suite:
# Install test dependencies
pip install pytest pytest-asyncio
# Run tests
pytest tests/ -v
# Run with coverage
pytest tests/ --cov=aiogram_dependency --cov-report=html
License
This project is licensed under the MIT License - see the LICENSE file for details.
Related Projects
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aiogram_dependency-1.0.1.tar.gz.
File metadata
- Download URL: aiogram_dependency-1.0.1.tar.gz
- Upload date:
- Size: 5.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0524313a96071947351101baad93768ec0b38bf472ab41e2fcc5d25a0254e812
|
|
| MD5 |
3c4666d77d097fcbc0207efc1d7f4dde
|
|
| BLAKE2b-256 |
70e1d0c5223d104ccff7306e1bf4f831a26d34e0924113b34ef7f39a274cf40e
|
Provenance
The following attestation bundles were made for aiogram_dependency-1.0.1.tar.gz:
Publisher:
release.yaml on AstralMortem/aiogram-dependency
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aiogram_dependency-1.0.1.tar.gz -
Subject digest:
0524313a96071947351101baad93768ec0b38bf472ab41e2fcc5d25a0254e812 - Sigstore transparency entry: 299423094
- Sigstore integration time:
-
Permalink:
AstralMortem/aiogram-dependency@76975b1df1eb037f14898a5b860c540e099aacf6 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/AstralMortem
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@76975b1df1eb037f14898a5b860c540e099aacf6 -
Trigger Event:
release
-
Statement type:
File details
Details for the file aiogram_dependency-1.0.1-py3-none-any.whl.
File metadata
- Download URL: aiogram_dependency-1.0.1-py3-none-any.whl
- Upload date:
- Size: 7.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4a7a9ff9eb726553a6aba7e1e0e69a1e002baccc05802ae8ec3324e4641da44
|
|
| MD5 |
3b6a0bdb5596f16287d4bd10147c30e7
|
|
| BLAKE2b-256 |
353732206eb1cd981e89e684f14152940be8082a98171795d9eb8afb1a50f980
|
Provenance
The following attestation bundles were made for aiogram_dependency-1.0.1-py3-none-any.whl:
Publisher:
release.yaml on AstralMortem/aiogram-dependency
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aiogram_dependency-1.0.1-py3-none-any.whl -
Subject digest:
e4a7a9ff9eb726553a6aba7e1e0e69a1e002baccc05802ae8ec3324e4641da44 - Sigstore transparency entry: 299423124
- Sigstore integration time:
-
Permalink:
AstralMortem/aiogram-dependency@76975b1df1eb037f14898a5b860c540e099aacf6 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/AstralMortem
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@76975b1df1eb037f14898a5b860c540e099aacf6 -
Trigger Event:
release
-
Statement type: