A dependency injection library for Python, Optimized for serverless applications
Project description
ididi
Introduction
ididi is a zero-configuration, minimal-code-intrusiveness dependency injection library for Python that works out of the box.
It allows you to define dependencies in a declarative way without any boilerplate code.
Install
pip install ididi
To view viusal dependency graph, install graphviz
pip install ididi[graphviz]
Usage
Decorate your top level dependencies and leave the rest to ididi
from didi import DependencyGraph
dg = DependencyGraph()
class Config:
def __init__(self, env: str = "prod"):
self.env = env
@dg.node(reuse=False) # disable reuse for database, it would be created every time
class Database:
def __init__(self, config: Config):
self.config = config
class Cache:
def __init__(self, config: Config):
self.config = config
class UserRepository:
def __init__(self, db: Database, cache: Cache):
self.db = db
self.cache = cache
class AuthService:
def __init__(self, db: Database):
self.db = db
class UserService:
def __init__(self, repo: UserRepository, auth: AuthService):
self.repo = repo
self.auth = auth
@dg.node # so that ididi knows AuthService should be created by this
def auth_service_factory(database: Database) -> AuthService:
return AuthService(db=database)
service = dg.resolve(UserService)
assert isinstance(service.repo.db, Database)
assert isinstance(service.repo.cache, Cache)
assert isinstance(service.auth.db, Database)
assert service.auth.db is service.repo.db
Automatic dependencies injection
from ididi import entry
class EmailService: ...
class EventStore: ...
@entry
def main(email: EmailService, es: EventStore) -> str:
assert isinstance(email, EmailService)
assert isinstance(es, EventStore)
assert email.user.auth.db.config.env == es.db.config.env
return "ok"
assert main() == "ok"
Usage with FastAPI
from fastapi import FastAPI
from ididi import DependencyGraph
app = FastAPI()
dg = DependencyGraph()
class AuthService: ...
@dg.node
def auth_service_factory() -> AuthService:
return AuthService()
Service = ty.Annotated[AuthService, Depends(dg.factory(auth_service_factory))]
@app.get("/")
def get_service(service: Service):
return service
Visualize the dependency graph(beta)
from ididi import DependencyGraph, Visualizer
dg = DependencyGraph()
vs = Visualizer(dg)
class ConfigService:
def __init__(self, env: str = "test"):
self.env = env
class DatabaseService:
def __init__(self, config: ConfigService):
self.config = config
class CacheService:
def __init__(self, config: ConfigService):
self.config = config
class BaseService:
def __init__(self, db: DatabaseService):
self.db = db
class AuthService(BaseService):
def __init__(self, db: DatabaseService, cache: CacheService):
super().__init__(db)
self.cache = cache
class UserService:
def __init__(self, auth: AuthService, db: DatabaseService):
self.auth = auth
self.db = db
class NotificationService:
def __init__(self, config: ConfigService):
self.config = config
class EmailService:
def __init__(self, notification: NotificationService, user: UserService):
self.notification = notification
self.user = user
dg.static_resolve(EmailService)
vs.view # use vs.view in jupyter notebook, or use vs.save(path, format) otherwise
Lazy Dependency(Beta)
when a node is defined as 'lazy', each of its dependency will be delayed to be resolved as much as possible.
Note that 'lazy' is transitive, if ServiceA
is lazy, and ServiceA
depends on ServiceB
, then ServiceB
is also lazy.
class UserRepo:
def __init__(self, db: Database):
self._db = db
def test(self):
return "test"
@dg.node(lazy=True)
class ServiceA:
def __init__(self, user_repo: UserRepo, session_repo: SessionRepo):
self._user_repo = user_repo
self._session_repo = session_repo
assert isinstance(self._user_repo, LazyDependent)
assert isinstance(self._session_repo, LazyDependent)
@property
def user_repo(self) -> UserRepo:
return self._user_repo
@property
def session_repo(self) -> SessionRepo:
return self._session_repo
assert isinstance(instance.user_repo, LazyDependent)
assert isinstance(instance.session_repo, LazyDependent)
assert instance.user_repo.test() == "test" # user_repo would be resolved when user_repo.test is accessed.
Runtime override
dg = DependencyGraph()
class Inner:
def __init__(self, value: str = "inner"):
self.value = value
@dg.node
class Outer:
def __init__(self, inner: Inner):
self.inner = inner
# Override nested dependency
instance = dg.resolve(Outer, inner=Inner(value="overridden"))
assert instance.inner.value == "overridden"
Features
- Automatically resolve dependencies
- Circular dependencies detection
- Support Forward References
Resolve Rules
- If a node has a factory, it will be used to create the instance.
- Otherwise, the node will be created using the
__init__
method.- Parent's
__init__
will be called if no__init__
is defined in the node.
- Parent's
- bulitin types are not resolvable by nature, it requires default value to be provided.
- runtime override with
dg.resolve
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
File details
Details for the file ididi-0.2.3.tar.gz
.
File metadata
- Download URL: ididi-0.2.3.tar.gz
- Upload date:
- Size: 61.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5a0949a6a5f50768dbff4ed14fd94f9a2120bac2d2ed655abef1c3bc1f2fc566 |
|
MD5 | 65612b91103904720918d9bef1ad3d86 |
|
BLAKE2b-256 | f80a0b70acfd9739590663736fcc49f84490d34b176812bd8414d3877b1694b8 |
File details
Details for the file ididi-0.2.3-py3-none-any.whl
.
File metadata
- Download URL: ididi-0.2.3-py3-none-any.whl
- Upload date:
- Size: 18.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | af2eff8bc4358e788c2b9845bd145c4e7fc90b840c33c01342d0b2383b9c8c91 |
|
MD5 | ea63560ee255cb78274c8df582ca660c |
|
BLAKE2b-256 | ae7500f342066a794b42977baa346377d0ab8239b1333b329a855e67276652ff |