Package: pythonioc
This package provides a simple inversion-of-control container.Install withpipinstallpythoniocQuick StartThe main idea is: services are registered to a service-registry and can be injected into users of that service (which can of course be services themselves).You have two options:use a global registry (never create a registry yourself)use a local registryExamples are below, more details will follow.Global Registryimportpythonioc# register your service with a default name (here: 'someService', see Notes On Names)@pythonioc.ServiceclassSomeService(object):# called when the service is auto-instantiated.defpostInit(self):pass@pythonioc.NamedService('DifferentNameService')classDifferentService(object):pass# for classes which we cannot decorate:pythonioc.registerService(ExternalService)# when we don't even have the class (or don't care about lazy-initialization)pythonioc.registerServiceInstance(SomeService())classServiceUser(object):# inject the dependency by classservice=pythonioc.Inject(SomeService)# inject the dependency by name (for cyclic dependencies)service2=pythonioc.Inject('DifferentNameService')myUser=ServiceUser()myUser.service# --> automatically created and injected service instance.# explicitly get a servicepythonioc.getService(SomeService)pythonioc.getService('DifferentNameService')Local RegistryclassService(object):# this will make the service registry inject a service named "someOtherService" which# comes from class SomeOtherService_someOtherService=Nonedef__init__(self):pass# will be called after everything is injecteddefpostInit(self):pass# will be called right before the object is destroyed (the registry's clean# method is called)defpreDestroy(self):passclassSomeOtherService(object):pass# let's register our servicesreg=ServiceRegistry()reg.registerService(Service)reg.registerService(SomeOtherService)Once everything is registered, a service can be injected byclassWiredUser(object):_service=Nonedef__init__(self,*args):passwiredUser=reg.createWired(WiredUser,'arg1','arg2')Wired objects are not automatically part of the service registry, only
if added by callingreg.registerServiceInstance.Wired objects can inject their own service registry, so they can create wired objects on the fly:classWiredUser(object):_service=NoneclassUserCreator(object):_serviceRegistry=NonedefcreateUser(self):returnself._serviceRegistry.createWired(WiredUser)userCreator=reg.createWired(UserCreator)# create some wired usersuserA=userCreator.createUser()userB=userCreator.createUser()Notes on NamesServices added to the registry need a name. If no name is provided, the class’ name (or the instance’s class name) is used. The name’s first character is lowered by convention.Example:importpythoniocclassMyService(object):passpythonioc.registerService(MyService)# --> name is 'myService'pythonioc.registerServiceInstance(MyService())# --> name is 'myService'pythonioc.registerService(MyService,serviceName='customName')# --> name is 'customName'pythonioc.registerServiceInstance(MyService(),serviceName='customName2')# --> name is 'customName2'Notes on Dependency CyclesGenerally, dependency cycles are resolved by lazily initializing services. Critical cycles can still occur, when two services depend on each other within theirpostInit-methods that are executed after initialization. Those cycles are detected by keeping track of the currently running postInit methods and throwing an exception.The service creation is thread safe, so accessing a non-initialized service with a long-running init or postInit method from two different threads will block one of the threads.BugsBug reports, suggestions for improvements etc. are always welcome!