Created
October 1, 2023 23:49
-
-
Save maces/b85103a7477ca51de40649e6f7e618de to your computer and use it in GitHub Desktop.
A VERY simple dependency injection example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
A VERY basic dependency injection experiment. | |
This does not have to make sense to everyone ;). | |
""" | |
from os import environ | |
from typing import Protocol, runtime_checkable | |
from pydantic import BaseSettings | |
##### MY DI ##### | |
# can be done in a class as well if preferred | |
my_di_registry: dict[str, any] = {} | |
@runtime_checkable | |
class DiAble(Protocol): | |
@classmethod | |
def construct(cls): | |
# convention for "complex" constructors | |
... | |
def d(func_name: str): | |
try: | |
return my_di_registry[func_name] | |
except IndexError as e: | |
raise Exception(f"Got no instance for {func_name}") from e | |
def di_register(cls: DiAble) -> DiAble: | |
instance = cls.construct() if hasattr(cls, "construct") else cls() | |
my_di_registry[cls.__name__] = instance | |
return cls | |
##### The App ##### | |
class MyBaseClass(DiAble): | |
my_state: dict | |
def __init__(self, settings: BaseSettings): | |
self.settings = settings | |
@staticmethod | |
def construct(cls): | |
return cls() | |
def my_shared_logic(self, param1): | |
... | |
def run(self): | |
print("running") | |
@di_register | |
class GetMeLazy: | |
def lazy(self): | |
return "lazy" | |
@di_register | |
class SettingsOne(BaseSettings): | |
... | |
@di_register | |
class SettingsAnother(BaseSettings): | |
... | |
@di_register | |
class SpecificClassA(MyBaseClass): | |
@classmethod | |
def construct(cls): | |
return cls(d("SettingsOne")) | |
def doing_something(self, something=d("GetMeLazy")): | |
return something | |
def run(self): | |
very = self.doing_something() | |
print(very.lazy()) | |
print("running") | |
@di_register | |
class SpecificClassB(MyBaseClass): | |
@classmethod | |
def construct(cls): | |
return cls(d("SettingsAnother")) | |
##### Run It ##### | |
def get_settings() -> str: | |
return environ.get("MY_SELECTOR") # or use a cli param instead | |
class MyRunner: | |
# A context manager, because we can | |
def __enter__(self): | |
return self | |
def __call__(self, transport: MyBaseClass): | |
self.transport = transport | |
return self | |
def __exit__(self, exc_type, exc_val, exc_tb): | |
# do cleanup here | |
... | |
def main(): | |
transport_to_choose = get_settings() | |
transport_runner = MyRunner() | |
with transport_runner(d(transport_to_choose)) as tr: | |
tr.transport.run() | |
if __name__ == "__main__": | |
main() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment