A small dependency injection Python library
Project description
Tidi
A small dependency injection Python library.
Inspired by Kent Tong's
disl and
FastAPI's Depends
.
Motivation
I found myself wanting to learn more about how dependency injection can be done in a pythonic way, with type-hinting, so had the itch to develop (yet another) library for it and share it as open source 🧑💻✌️
Read more about the motivation in this Medium article.
Primary API
The top level import of tidi
provides everything needed it's primary intended
use.
@tidi.inject
- a decorator that will replace certain keyword arguments with dependencies, based on their type & if they haven't been passed intidi.Injected[DependencyClass]
- a type alias, wrapping typing.Annotated, that indicates that a keyword argument should be injectedtidi.register(dependency_instance)
- a function that registers an object to be available for injection as a dependencytidi.Provider(get_dependency_function)
- a wrapper class around a function that will be called to provide a dependencytidi.UNSET
- a sentinel object to indicate that a dependency should be loaded from the registrytidi.field_factory(DependencyClass)
- a helper function for injecting dependencies into dataclass fields
Example of use
Consider a micro-sized interactive CLI that lets a user choose a handbag then search through it,
# search-handbag.py
import tidi
from handbags import Handbag, HandbagItem, load_handbag
@tidi.inject
def dig_through_handbag(
item_type: str,
handbag: tidi.Injected[Handbag] = tidi.UNSET,
) -> HandbagItem | None:
return handbag.get_items_by_type(item_type).first(default=None)
def init_handbag():
selected_handbag = input("Select a handbag: ")
tidi.register(load_handbag(selected_handbag))
def run_search():
item_type = input("What are you looking for? ")
# ⬇️ registered `Handbag` instance gets injected ✨
item = dig_through_handbag(item_type)
if item is None:
print("Uh oh, can't find it 🤷♀️, try again")
run_search()
else:
print(f"We're in luck! Here's your {item.name} 😎")
if __name__ == "__main__":
init_handbag()
run_search()
Running it looks something like this,
$ python search-handbag
Select a handbag: BCBGMAXAZRIA
What are you looking for? Nail file
Uh oh, can't find it 🤷♀️, try again
What are you looking for? Lip balm
We're in luck! Here's your Blistex 😎
We can see Dependency Injection happening hear to achieve Inversion of Control and obey the Law of Demeter.
dig_through_handbag
isn't responsible for creating aHandbag
and doesn't require its caller to know about it, rather aHandbag
is injected ✨init_handbag
creates theHandbag
, but doesn't need to return it. An example of separating the app initialisation from the main logic.run_search
doesn't need to know about anything that it doesn't use (in this case theHandbag
), obeying the Law of Demeter.
When testing,
- a mock
Handbag
could be passed in as a keyword argument to testdig_through_handbag
, and - patching
dig_through_handbag
with a stub could be done to testrun_search
with no requirement for a mockHandbag
.
More examples
You can find some executable examples in the demo/ directory of the repo.
Also see the Usage documentation for more examples.
Interested in contributing?
Feel free to create an issue or author a PR 😊
For the latter, check out the CONTRIBUTING guide for a quick start on development.
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
Built Distribution
File details
Details for the file tidi-0.3.0.tar.gz
.
File metadata
- Download URL: tidi-0.3.0.tar.gz
- Upload date:
- Size: 9.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.0 CPython/3.9.19
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 2c5e0d8d556a6a7ec4e1df318b44ef1afcf8f5d77a11593796704d85e2ee43a6 |
|
MD5 | 410f60512fa6300ce2a75ce0214b39fb |
|
BLAKE2b-256 | 582655ab377b5ba341aee81c3ec68a3528b7932fbdd3ec82cb0e1ee4dc81b975 |
File details
Details for the file tidi-0.3.0-py3-none-any.whl
.
File metadata
- Download URL: tidi-0.3.0-py3-none-any.whl
- Upload date:
- Size: 9.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.0 CPython/3.9.19
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | ba7f023b03bc491a5e2b49ff7eef743755f04b420a74f171e7636c9ee21a043f |
|
MD5 | 983ff6292f4da5ce3968bfe18cd4b483 |
|
BLAKE2b-256 | b09963166293a0a55c82b9572cc0e8d9039d012796de095dc979600f8374e584 |