Lidi is a lightweight dependency injector designed to simplify dependency management in your Python projects. It provides a simple and intuitive API for binding classes and resolving dependencies.
You can install Lidi using pip:
pip install lidipy
To bind a class to an instance or a callable, you can use the bind()
method of the Lidi
instance:
from lidipy import Lidi
class Parent:
pass
class Child:
pass
lidi = Lidi()
# bind instance
lidi.bind(Parent, Child())
# or bind a callable
lidi.bind(Parent, Child)
If you want to bind a class as a singleton, you can pass the singleton
parameter as True
when calling the bind()
method:
lidi.bind(Parent, Child(), singleton=True)
With singleton binding, the same instance of the class will be returned every time it is resolved.
To resolve a class and its dependencies, you can use the resolve()
method of the Lidi class:
instance = lidi.resolve(Parent) # instance is Child()
If the class was bound as a singleton, the same instance will be returned each time it is resolved.
Lidi also supports deferred resolution using the resolve_defer()
method. This method returns a callable that, when invoked, resolves the class:
deferred_resolve = lidi.resolve_defer(Parent)
instance = deferred_resolve()
Class attributes are often services or repositories. Lidi supports resolution of bindings using the resolve_attr
method.
lidi.bind(Parent, Child)
class Repository:
service: Parent = lidi.resolve_attr(Parent) # instance is Child()
If a binding is missing for a requested type, a BindingMissing
exception will be raised.
You can handle this exception and provide appropriate error handling in your application.
from lidipy import BindingMissing
try:
instance = lidi.resolve(Mother)
except BindingMissing as e:
print(e) # Outputs: "Binding missing for type: Mother"
Lidi can be used seamlessly with Python's dataclasses. Here's an example of how to use dataclasses with Lidi:
from dataclasses import dataclass
from lidipy import Lidi
lidi = Lidi()
@dataclass(frozen=True)
class Config:
db_url: str
# Bind Config
lidi.bind(Config, Config(db_url="example.com:5432"))
@dataclass
class Database:
config: Config = lidi.resolve(Config) # or lidi.resolve_defer(Config)
def connect(self):
print(f"Connecting to database at {self.config.db_url}")
# Bind Database
lidi.bind(Database, Database)
# Resolve the dataclass with dependencies
database = lidi.resolve(Database)
database.connect() # Output: Connecting to database at example.com:5432
Lidi supports bindings change on runtime, here's an example:
from dataclasses import dataclass, field
from lidipy import Lidi
lidi = Lidi()
@dataclass
class Config:
db_url: str
@dataclass
class Database:
config: Config = field(default_factory=lidi.resolve_defer(Config))
def connect(self):
print(f"Connecting to database at {self.config.db_url}")
# Bind the initial dependencies
lidi.bind(Config, Config(db_url="example.com:5432"))
lidi.bind(Database, Database)
# Initial resolve
database = lidi.resolve(Database)
database.connect() # Output: Connecting to database at example.com:5432
# Dynamically change binding
lidi.bind(Config, Config(db_url="other-example.com:5432"))
# Second resolve
database = lidi.resolve(Database)
database.connect() # Output: Connecting to database at other-example.com:5432
Contributions are welcome! If you find a bug or want to suggest an improvement, please open an issue or submit a pull request on the GitHub repository.
Lidi is licensed under the MIT License. Feel free to use, modify, and distribute this project as per the terms of the license.