Package: touchstone
ContentsWhat is Touchstone?Learn by ExampleAuto WiringInterface BindingBinding with Factory MethodsBinding SingletonsContextual BindingDjango SupportCelery TasksWhat is Touchstone?Touchstone is an annotations-driven Inversion of Control container for
Python 3.6 and above.Links:GitHubLearn by ExampleAuto WiringfromtouchstoneimportContainerclassChild:passclassParent:def__init__(self,child:Child)->None:self.child=childcontainer=Container()parent=container.make(Parent)assertisinstance(parent.child,Child)Interface BindingfromtouchstoneimportContainerclassAbstractChild:passclassChild(AbstractChild):passclassParent:def__init__(self,child:AbstractChild)->None:self.child=childcontainer=Container()container.bind(AbstractChild,Child)parent=container.make(Parent)assertisinstance(parent.child,Child)Binding with Factory MethodsfromtouchstoneimportContainerclassChild:def__init__(self,name:str)->None:self.name=nameclassParent:def__init__(self,child:Child)->None:self.child=childcontainer=Container()container.bind(Child,lambda:Child('them'))parent=container.make(Parent)assertisinstance(parent.child,Child)assertparent.child.name=='them'Binding SingletonsfromtouchstoneimportContainer,SINGLETONclassChild:def__init__(self,name:str)->None:self.name=nameclassParent:def__init__(self,child:Child)->None:self.child=childcontainer=Container()them_child=Child('them')container.bind_instance(Child,them_child)# Or...container.bind(Child,lambda:them_child,lifetime_strategy=SINGLETON)parent=container.make(Parent)assertisinstance(parent.child,Child)assertparent.childisthem_childContextual BindingfromtouchstoneimportContainerclassChild:def__init__(self,name:str)->None:self.name=nameclassParent:def__init__(self,child1:Child,child2:Child)->None:self.child1=child1self.child2=child2container=Container()container.bind_contextual(when=Parent,wants=Child,wants_name='child1',give=lambda:Child('her'))container.bind_contextual(when=Parent,wants=Child,wants_name='child2',give=lambda:Child('him'))parent=container.make(Parent)assertisinstance(parent.child1,Child)assertisinstance(parent.child2,Child)assertparent.child1.name=='her'assertparent.child2.name=='him'Django SupportNow featuring Django support! New in v0.3.0Configure your instance oftouchstone.Containerhowever you see fit.In your mainsettings.py, setTOUCHSTONE_CONTAINER_GETTERto
the path to a callable that will return the singleton instance oftouchstone.Containeryour app uses.Note that yourgettershould build theContainerprecisely once, and
then return that sameContaineron all subsequent calls. Build it as a
singleton.To get injected properties in your class-based views:In your mainsettings.py, addtouchstone.django.InjectViewsMiddlewareto yourMIDDLEWARElist.Use class annotations on your class-based views. Cached Properties will be
added to your view classes so that they automatically resolve using your
configured touchstone container. For example:classMyView(View):something:MyObjectdefget(self,request):# You can now access self.something!To get injected properties in your middleware, you’ll need to do a
little more work because we haven’t found a good way to hook into
Django’s middleware instantiation logic.fromtouchstone.djangoimportinject_magic_properties@inject_magic_propertiesclassMyMixin:something:MyObject# define your mixin here...# You'll be able to use `self.something` from within every instance method.Celery TasksCelery tasks called can be applied to any callable with a decorator. For example:classMyLogger:def__init__(self,another_logger:AnotherLogger):self.another_logger=another_loggerdeflog(self,msg):print(msg)self.another_logger.log(msg)@app.taskdeflog_messages(msg):another_logger=AnotherLogger()my_logger=MyLogger(another_logger)my_logger.log(msg)log_messages.apply_async(args=['hello world'])However, if we want to refactor this code to make use of touchstone as a service locator:classMyLogger:def__init__(self,another_logger:AnotherLogger):self.another_logger=another_loggerdeflog(self,msg):print(msg)self.another_logger.log(msg)@app.taskdeflog_messages(msg):container=get_container()my_logger=container.make(MyLogger)my_logger.log(msg)log_messages.apply_async(args=['hello world'])Each task would then have quite a bit of repeated code to create the container for each task and then
make class instance we need.classMyLogger:def__init__(self,another_logger:AnotherLogger):self.another_logger=another_loggerdeflog(self,msg):print(msg)self.another_logger.log(msg)@touchstone_taskclassLogMessagesTask:my_logger:MyLoggerdefrun(self,msg):self.my_logger.log(msg)LogMessagesTask.apply_async(args=['hello world'])