Skip to content

Instantly share code, notes, and snippets.

@maces
Created October 1, 2023 23:49
Show Gist options
  • Save maces/b85103a7477ca51de40649e6f7e618de to your computer and use it in GitHub Desktop.
Save maces/b85103a7477ca51de40649e6f7e618de to your computer and use it in GitHub Desktop.
A VERY simple dependency injection example
"""
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