Skip to content

Lidi - A LIghtweight Dependency Injector for Python

License

Notifications You must be signed in to change notification settings

AlTosterino/Lidi

Repository files navigation

Build status Coverage Status Code style: black Ruff Imports: isort Checked with mypy

Lidi (LIghtweight Dependency Injector)

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.

Installation

You can install Lidi using pip:

pip install lidipy

Usage

Basic Binding

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)

Singleton Binding

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.

Resolving Dependencies

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.

Deferred Resolution

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 Attribute Resolution

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()

Handling Missing Bindings

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"

Usage with Dataclasses

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

Dynamic binds on runtime

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

Contributing

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.

License

Lidi is licensed under the MIT License. Feel free to use, modify, and distribute this project as per the terms of the license.