#!/usr/bin/env python2 # -*- coding: utf-8 -*- """ Created on Sat Jun 23 06:53:56 2018 @author: s """ # Global registry for instance registration resource_registry = {} def provides(name): """Class decorator for an instance provider. The name passed in is what the attribute is called. The instance will be auto registered in the registry with the value of the handle TODO : the name could also be a function which could return the attribute name to extract the instance name from """ def wrapper(cls): def init_and_register(init): def inner(self,*args,**kwargs): "The new wrapper constructor" # Call the old constructor init(self,*args,**kwargs) # Update the resource registry with the desired key and instance resource_registry[self.__dict__[name]] = self return inner # Replace the constructor with a wrapped constructor cls.__init__ = init_and_register(cls.__init__) return cls return wrapper def requires(*resource_types): """ Class decorator to inject instances from the registry into the instance under construction""" def wrap_constructor(self,*args,**kw): "The constructor wrapper" # First call the original constructor self.__oldinit__(*args,**kw) # If this instance requires dependencies to be injected if hasattr(self,'_resource_types') : # for each dependency for resource_type in self._resource_types: # inject the dependency directly as in instance variable # from the resource registry self.__dict__[resource_type] = resource_registry[kw[resource_type]] def inner(cls): cls._resource_types = resource_types cls.__oldinit__ = cls.__init__ cls.__init__ = wrap_constructor return cls return inner # The following class indicates a sample resource provider which could be # injected. The 'handle' passed to the class decorator is the name of the attribute # whole value will be used as the key to register the instance name in the registry @provides('handle') class Provider(object): def __init__(self,handle): self.handle = handle def __str__(self): return 'Provider(%s)' % self.handle # A sample user object (ie. the object into which the dependency is to be injected # The constructor needs to accept kw based arguments for the injection to happen # The requires decorator lists the attribute names under which the dependencies will # be injected into the instance of this class @requires('first','second') class User(object): def __init__(self, **kwargs): pass if __name__ == '__main__' : # Initialise the providers. They auto register themselves. p1 = Provider('one') p2 = Provider('two') # Instantiate the user. specify the key under which the # injected objects have been registered in the values of the # keyword arguments (the key being the attribute name declared # earlier in the class decorator) u = User(first = 'one', second = 'two') # Test that the dependencies have been appropriately injected assert u.first == p1 assert u.second == p2