Async dependency injection library
Project description
Async-first dependency injection library based on python type hints
Framework integrations:
Installation
Install using pip pip install aioinject
Example
import aioinject
class Database:
def __init__(self) -> None:
self._storage = {1: "Username"}
def get(self, id: int) -> str | None:
return self._storage.get(id)
class UserService:
def __init__(self, database: Database) -> None:
self._database = database
def get(self, id: int) -> str:
user = self._database.get(id)
if user is None:
raise ValueError
return user
container = aioinject.Container()
container.register(aioinject.Singleton(Database))
container.register(aioinject.Singleton(UserService))
def main() -> None:
with container.sync_context() as ctx:
service = ctx.resolve(UserService)
user = service.get(1)
assert user == "Username"
print(user)
if __name__ == "__main__":
main()
Injecting dependencies into a function
You can inject dependencies into a function using @inject decorator,
but that's usually only necessary if you're working with a framework:
import contextlib
from collections.abc import AsyncIterator
from contextlib import aclosing
from typing import Annotated
import uvicorn
from fastapi import FastAPI
from aioinject import Container, Inject, Singleton
from aioinject.ext.fastapi import AioInjectMiddleware, inject
class Service:
pass
container = Container()
container.register(Singleton(Service))
@contextlib.asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
async with aclosing(container):
yield
app = FastAPI(lifespan=lifespan)
app.add_middleware(AioInjectMiddleware, container=container)
@app.get("/")
@inject
async def root(service: Annotated[Service, Inject]) -> str:
return str(service)
if __name__ == "__main__":
uvicorn.run("main:app")
Using multiple dependencies with same type
If you have multiple implementations for the same dependency
you can use typing.NewType to differentiate between them:
import dataclasses
from typing import Annotated, NewType
from aioinject import Container, providers, inject, Inject
@dataclasses.dataclass
class Client:
name: str
GitHubClient = NewType("GitHubClient", Client)
GitLabClient = NewType("GitLabClient", Client)
def get_github_client() -> GitHubClient:
return GitHubClient(Client(name="GitHub Client"))
def get_gitlab_client() -> GitLabClient:
return GitLabClient(Client(name="GitLab Client"))
container = Container()
container.register(providers.Scoped(get_github_client))
container.register(providers.Scoped(get_gitlab_client))
with container.sync_context() as ctx:
github_client = ctx.resolve(GitHubClient)
gitlab_client = ctx.resolve(GitLabClient)
print(github_client, gitlab_client)
Working with Resources
Often you need to initialize and close a resource (file, database connection, etc...),
you can do that by using a contextlib.(async)contextmanager that would return your resource.
Aioinject would automatically close them when context is closed,
or when you call container.aclose() if your dependency is a Singleton.
import contextlib
from aioinject import Container, providers
class Session:
pass
@contextlib.contextmanager
def get_session() -> Session:
print("Startup")
yield Session()
print("Shutdown")
container = Container()
container.register(providers.Scoped(get_session))
with container.sync_context() as ctx:
session = ctx.resolve(Session) # Startup
session = ctx.resolve(Session) # Nothing is printed, Session is cached
# Shutdown
Async Dependencies
You can register async resolvers the same way as you do with other dependencies,
all you need to change is to use Container.context instead of Container.sync_context:
import asyncio
from aioinject import Container, providers
class Service:
pass
async def get_service() -> Service:
await asyncio.sleep(1)
return Service()
async def main() -> None:
container = Container()
container.register(providers.Scoped(get_service))
async with container.context() as ctx:
service = await ctx.resolve(Service)
print(service)
if __name__ == "__main__":
asyncio.run(main())
Providers
Scoped
Objects provided with Scoped provider would be cached within context.
Transient
Transient provider would provide different instances even if used within same context
Singleton
Singleton works as you expect - there would be only one instance of a singleton
object, bound to a specific container
Object
Object provider just returns an object provided to it, e.g. Object(42)
Project details
Release history Release notifications | RSS feed
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 aioinject-0.24.0.tar.gz.
File metadata
- Download URL: aioinject-0.24.0.tar.gz
- Upload date:
- Size: 23.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: pdm/2.12.2 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43f3d533d551dd235b0226e0bb7282292ab018e96dfb80363e50308a2dbd1129
|
|
| MD5 |
adc15850c9e41da7c20bf048e912bae2
|
|
| BLAKE2b-256 |
489f8942fb1dd99c7e850d9bf9165a6361eee4f3ba7938303bee7b41536153f7
|
File details
Details for the file aioinject-0.24.0-py3-none-any.whl.
File metadata
- Download URL: aioinject-0.24.0-py3-none-any.whl
- Upload date:
- Size: 16.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: pdm/2.12.2 CPython/3.11.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
34e679f2837fcd1ad92c5ddee69d04444238062b00e3b9c2ef4a2880fd016106
|
|
| MD5 |
d4de752694dc91bdb328ffbd716860c0
|
|
| BLAKE2b-256 |
147deb39fffb6e18f9eaf6f22305dc381bed0dbdc2877ed5c95f111f9d5755ca
|