super lightweight (only 35 lines) dependency injection (ioc) support for Python - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: General (https://python-forum.io/forum-1.html) +--- Forum: Code sharing (https://python-forum.io/forum-5.html) +--- Thread: super lightweight (only 35 lines) dependency injection (ioc) support for Python (/thread-39174.html) |
super lightweight (only 35 lines) dependency injection (ioc) support for Python - kenttong - Jan-13-2023 Hi, A super simple and risk-free way to do dependency injection (ioc) in Python. The entire code base is only 35 lines (empty lines included). No need to use a complex 3rd-party framework or be concerned about the long-term viability of the library. Please check it out at https://github.com/freemant2000/disl Thanks RE: super lightweight (only 35 lines) dependency injection (ioc) support for Python - Gribouillis - Jan-13-2023 This looks interesting. I still need to find a concrete example of where to use this in my code but I'll definitely try this one day or the other. Thanks for sharing! RE: super lightweight (only 35 lines) dependency injection (ioc) support for Python - kenttong - Jan-13-2023 (Jan-13-2023, 08:14 AM)Gribouillis Wrote: This looks interesting. I still need to find a concrete example of where to use this in my code but I'll definitely try this one day or the other. Thanks for sharing! I am glad that you find it useful. It is typically used when your code needs to get a configuration value (db Url, api key, etc.) or in service layer code (i.e., the code that gets called by your UI code to read or/and update the DB). Let me know if I can be of any help with the code RE: super lightweight (only 35 lines) dependency injection (ioc) support for Python - Gribouillis - Jan-13-2023 I've been playing with this, why not make the container class a subclass of dict with a custom attribute access? This makes the interface of the container more friendly and one can use dictionary operations to update it.from dataclasses import dataclass @dataclass class Inject: name: str = None class Wirer(dict): def __setattr__(self, name, bean): self[name] = bean def __getattr__(self, name): if name in self: b = self[name] # must do this first in case of mutual dependencies # also avoids further calls to __getattr__ object.__setattr__(self, name, b) self._wire_bean(name, b) return b else: raise AttributeError(name) def _wire_bean(self, name, b): if not hasattr(b, "__dict__"): # built-in object such as a string return b attrs = vars(b) for n, v in attrs.items(): if isinstance(v, Inject): dep_name = v.name or n attrs[n] = getattr(self, dep_name) if __name__ == '__main__': class ProductDb: def __init__(self): self.db_path=Inject("database_path") # specify the bean name def get_products(self): print(f"getting products from {self.db_path}") di = Wirer() di.pdb = ProductDb() di.database_path = "c:/Users/kent/test.db" di.pdb.get_products() di = Wirer(pdb=ProductDb(), database_path='c:/spam/spam.db') di.pdb.get_products() di.pdb.get_products()
RE: super lightweight (only 35 lines) dependency injection (ioc) support for Python - kenttong - Jan-14-2023 It looks really nice! Thanks RE: super lightweight (only 35 lines) dependency injection (ioc) support for Python - Gribouillis - Jan-14-2023 In a new version, I'm able to inject partial data class Spam: def __init__(self): self.eggs = Inject() self.ham = Inject() def __str__(self): return f"<Spam {self.eggs} {self.ham}>" spam = Wirer(spam=Spam(), ham='HAM').spam print(spam) spam = Wirer(spam=spam, eggs='EGGS').spam print(spam) In fact the Wirer behaves much like the builtin partial function. Instead of partial functions, it manipulates partial objects, or partially wired objects. This is a great paradigm!
|