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
singletonandfactorydecorators must be used in all classes that will be injected into the container:singletonmeans that every time an instance of this class is requested, the instance created in the first request will be used;factorymeans 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
injectandinject_propertydecorators 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;injectandinject_propertycan 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;injectonly implements the method, keeping its call as a function, whileinject_propertyturns the method into a property of the class's objects).
- The
is_lazyflag (which has the default valueFalse) when it has the valueTruemeans 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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6218d648629aeda11b6a84e1c74e69dae1c948ea9a22a20218598f88fb32a44b
|
|
| MD5 |
c8fa4e7d5d23e0fadfc1f9a4bf1d9888
|
|
| BLAKE2b-256 |
76b5419fd8279a347c8992fe976e5fb0ae62957568c9b7efe45e4fdc2042a3fe
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e93da18b62b35f0c0f0434d3766d70f5f5401e5ae4538d1ddb5a38285dd9783
|
|
| MD5 |
50aaffdcd3e7409170c316c2d6d24ad9
|
|
| BLAKE2b-256 |
a5fe8025b9a9652ed6f3832b61a1ad39e234ad10dc491820afc8905945088c53
|