Last active
February 22, 2020 21:12
-
-
Save vskrachkov/02ecc12cc38b1a55057d492ea8881854 to your computer and use it in GitHub Desktop.
Simple dependency injection sample. Python. Framework agnostic
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
import inspect | |
class Value: | |
__slots__ = ('name', 'default') | |
def __init__(self, name, default=None): | |
self.name = name | |
self.default = default | |
def create_provider(): | |
registry = {} | |
def provide(abstract, concrete): | |
if abstract in registry: | |
raise Exception( | |
f'Implementation for {abstract} is already provided' | |
) | |
registry[abstract] = concrete | |
def provided(abstract): | |
if abstract not in registry: | |
raise Exception(f'Implementation for {abstract} is not provided') | |
concrete = registry[abstract] | |
def resolve_dependencies(thing): | |
params = {} | |
for name, param in inspect.signature(thing).parameters.items(): | |
if isinstance(param.default, Value): | |
if param.default.name in registry: | |
params[name] = registry[param.default.name] | |
else: | |
params[name] = param.default.default | |
if param.annotation in registry: | |
dependency = registry[param.annotation] | |
params[name] = resolve_dependencies(dependency) | |
return params | |
if inspect.isfunction(concrete): | |
return concrete(**resolve_dependencies(concrete)) | |
if inspect.isclass(concrete): | |
return concrete(**resolve_dependencies(concrete.__init__)) | |
return concrete | |
def clear(abstract) -> None: | |
if abstract in registry: | |
del registry[abstract] | |
return provide, provided, clear | |
def main(): | |
class SmsSender: | |
def __init__(self, message=Value('message')): | |
self.message = message | |
def send(self): | |
raise NotImplementedError() | |
class SmsSenderAAA(SmsSender): | |
def send(self): | |
print(f'sms sender SmsSenderAAA --> {self.message}') | |
class SmsSenderBBB(SmsSender): | |
def send(self): | |
print(f'sms sender SmsSenderBBB: {self.message}') | |
provide, provided, clear = create_provider() | |
provide(SmsSender, SmsSenderAAA) | |
sms_sender = provided(SmsSender) | |
sms_sender.send() | |
clear(SmsSender) | |
provide(SmsSender, SmsSenderBBB) | |
sms_sender = provided(SmsSender) | |
sms_sender.send() | |
provide('token', 12) | |
print(provided('token')) | |
provide('message', 'some message text') | |
clear(SmsSender) | |
provide(SmsSender, SmsSenderBBB) | |
sms_sender = provided(SmsSender) | |
sms_sender.send() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment