Skip to main content

A simple reflection-based dependency injector.

Project description

subtle-wired

A simple reflection-based dependency injector. The name subtle-wired comes from the goal of making these connections between dependencies something subtle, light, and straightforward.

  • Lazy | eager execution
  • Thread-safe
  • Simple to use
  • Force to respect the Dependency Inversion Principle
  • Python 3

Usage example

1. Classes to be inserted into the container:

Every class needs to have an interface to be injected (which will ensure that the dependency inversion principle is followed):

from abc import ABC
from dataclasses import dataclass
from typing import Any


@dataclass
class User:
    id: str
    name: str


class IConfig(ABC):
    def user_endpoint(self) -> str:
        pass

    def base_url(self) -> str:
        pass


class IHttpService(ABC):
    def get(self, endpoint: str, parameters: dict[str, str]) -> dict[str, Any]:
        pass


class IUserRepository(ABC):
    def get_user(self, user_id: str) -> User:
        pass

Below is a simplified implementation example for the previous interfaces:

from subtle_wired.decorators import (
    singleton,
    factory,
    inject,
    inject_property
)


@singleton()
class Config(IConfig):
    def user_endpoint(self) -> str:
        return "/user"

    def base_url(self) -> str:
        return "https://www.backend.com/v1"


@factory(is_lazy=True)
class HttpService(IHttpService):
    def __init__(self, config: IConfig):
        self.__config = config

    def get(self, endpoint: str, parameters: dict[str, str]) -> dict[str, Any]:
        url = self.__config.base_url() + endpoint
        response = requests.get(url, params=parameters)
        return response.json()


@singleton()
class UserRepository(IUserRepository):
    @inject_property
    def __http_service(self) -> IHttpService: ...

    @inject
    def __init__(self, config: IConfig):
        self.__config = config

    def get_user(self, user_id: str) -> User:
        endpoint = self.__config.user_endpoint()
        result = self.__http_service.get(
            endpoint,
            {
                "user_id": user_id
            }
        )
        return User(
            id=result["id"],
            name=result["name"]
        )

Explanation:

  • The singleton and factory decorators must be used in all classes that will be injected into the container:
    • singleton means that every time an instance of this class is requested, the instance created in the first request will be used;
    • factory means that every time an instance of this class is requested, a new instance will be created (note that this does not mean that the class is a factory, but rather that a factory within the container will generate it).
  • The inject and inject_property decorators are decorators for searching for instances within the container:
    • inject, when applied to class constructors, searches for instances for the parameters of this constructor. It is not mandatory in class constructors that are within the container;
    • inject and inject_property can be applied to methods (other than the constructor) to search for instances of the method's return in the container (the difference is in usage; inject only implements the method, keeping its call as a function, while inject_property turns the method into a property of the class's objects).
  • The is_lazy flag (which has the default value False) when it has the value True means that an instance of the class will only be created in the container the first time it is requested (whether by eager classes internal or external to the container).

2. Creating the container with the list of component classes:

Example of a container for dependency injection:

from subtle_wired.core import Container


class MyContainer(Container):
    components = [
        Config,
        UserRepository,
        HttpService,
    ]

3. Container usage:

Example of how to use the created container:

from subtle_wired.core import Injector


MyContainer()\
    .startup()

user_repository = Injector()\
    .get_instance(IUserRepository)

print("User Name:", user_repository.get_user("U123"))

Another way to use the instances injected by the container is to inject them into classes external to the container (this way, you don't need to use the Injector directly - but rather the inject decorator):

from subtle_wired.decorators import inject


MyContainer()\
    .startup()

class OutsideTheContainer:
    @inject
    def __init__(self, user_repository: IUserRepository):
        self.__user_repository = user_repository

    def user_name(self, user_id: str) -> str:
        return self.__user_repository.get_user(user_id).name


print("User Name:", OutsideTheContainer().user_name("U123"))

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

subtle_wired-1.0.0.tar.gz (6.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

subtle_wired-1.0.0-py3-none-any.whl (9.8 kB view details)

Uploaded Python 3

File details

Details for the file subtle_wired-1.0.0.tar.gz.

File metadata

  • Download URL: subtle_wired-1.0.0.tar.gz
  • Upload date:
  • Size: 6.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.0.1 CPython/3.9.22 Linux/5.4.0-215-generic

File hashes

Hashes for subtle_wired-1.0.0.tar.gz
Algorithm Hash digest
SHA256 6218d648629aeda11b6a84e1c74e69dae1c948ea9a22a20218598f88fb32a44b
MD5 c8fa4e7d5d23e0fadfc1f9a4bf1d9888
BLAKE2b-256 76b5419fd8279a347c8992fe976e5fb0ae62957568c9b7efe45e4fdc2042a3fe

See more details on using hashes here.

File details

Details for the file subtle_wired-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: subtle_wired-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 9.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.0.1 CPython/3.9.22 Linux/5.4.0-215-generic

File hashes

Hashes for subtle_wired-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1e93da18b62b35f0c0f0434d3766d70f5f5401e5ae4538d1ddb5a38285dd9783
MD5 50aaffdcd3e7409170c316c2d6d24ad9
BLAKE2b-256 a5fe8025b9a9652ed6f3832b61a1ad39e234ad10dc491820afc8905945088c53

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page