Glorpen IOC Container¶
Another Dependency Injection library for Python.
This package has following three guidelines:
- any class configured by Container mechanism should not be modified in any way
- there is no need for external services definition files for Container
- no Container compiling and service tagging - we have introspection and dynamic language for this task
And so this package provides:
- no xml configuration
- no annotations (more cons than pros)
- no changes to services code
Official repositories¶
For forking and other funnies.
BitBucket: https://bitbucket.org/glorpen/glorpen-di
Supported design patterns¶
Service instance can be created by:
- factory service
- calling class object with arguments
Instance options can be altered by:
- constructor arguments
- setters
- calling methods
- using configurator service
Each service has defined scope, service cannot request other service from narrower scope.
Contents¶
glorpen.di
API Documentation¶
glorpen.di
¶
Dependency injection component.
-
glorpen.di.
__version__
¶ Current package version.
-
class
glorpen.di.
Container
[source]¶ Alias to
glorpen.di.container.Container
.
glorpen.di.container
¶
-
class
glorpen.di.container.
Container
[source]¶ Implementation of DIC container.
-
add_service
(name)[source]¶ Adds service definition to this container.
name argument should be a class, import path, or string if
Service.implementation()
will be used.- Returns:
Service
-
set_scope_hierarchy
(*scopes)[source]¶ Sets used scopes hierarchy.
Arguments should be scopes sorted from widest to narrowest. A service in wider scope cannot request service from narrower one.
Default is: [
glorpen.di.scopes.ScopeSingleton
,glorpen.di.scopes.ScopePrototype
].- Args:
- classes or instances of
glorpen.di.scopes.ScopeBase
-
-
class
glorpen.di.container.
Deffered
(service=None, method=None, param=None)[source]¶ Class for marking values for lazy resolving.
Values are resolved by
Container
upon service creation.
-
class
glorpen.di.container.
Service
(name_or_impl)[source]¶ Service definition.
When filling arguments for constructor and method calls you can use:
- my_var__svc=MyClass - will inject service MyClass to my_var
- my_var__param=”my.param” - will inject parameter named “my.param” to my_var
Implementation value for service can be:
- class instance
- string with import path
- callable
-
call
(method, kwargs=None, **kwargs_inline)[source]¶ Adds a method call after service creation with given arguments.
- Returns:
Service
-
call_with_signature
(method, kwargs=None, **kwargs_inline)[source]¶ Adds a method call after service creation with given arguments. Arguments detected from function signature are added if not already present.
- Returns:
Service
-
configurator
(service=None, method=None, callable=None, kwargs=None, **kwargs_inline)[source]¶ Adds service or callable as configurator of this service.
- Args:
- service + method, callable: given service method/callable will be called with instance of this service.
- Returns:
Service
-
factory
(service=None, method=None, callable=None, kwargs=None, **kwargs_inline)[source]¶ Sets factory callable.
- Returns:
Service
-
kwargs_from_signature
()[source]¶ Adds arguments found in class signature, based on provided function hints.
- Returns:
Service
glorpen.di.scopes
¶
Built-in scopes.
glorpen.di.exceptions
¶
Exceptions used by glorpen.di
.
-
exception
glorpen.di.exceptions.
InjectionException
(svc_name, cls, method_name=None)[source]¶ Raised when service or its method cannot be created or called
-
exception
glorpen.di.exceptions.
InvalidAliasTargetException
(name)[source]¶ Raised when adding alias to alias or not exisiting service
-
exception
glorpen.di.exceptions.
RecursionException
(s_def, requester_chain)[source]¶ Raised when service definition is requiring itself.
-
exception
glorpen.di.exceptions.
ScopeWideningException
(s_def, requester_chain)[source]¶ Raised when Service A depends on Service B from narrower scope.
-
exception
glorpen.di.exceptions.
ServiceAlreadyCreated
(svc_name)[source]¶ Raised when service definition is changed but service is already created by
glorpen.di.container.Container
.
-
exception
glorpen.di.exceptions.
UnknownParameterException
(name)[source]¶ Raised when requesting parameter which is not registered in
glorpen.di.container.Container
.
-
exception
glorpen.di.exceptions.
UnknownScopeException
(scope, svc_name)[source]¶ Raised when service has scope not assigned to
glorpen.di.container.Container
byglorpen.di.container.Container.set_scope_hierarchy()
.
-
exception
glorpen.di.exceptions.
UnknownServiceException
(svc_name)[source]¶ Raised when requesting service name which is not registered in
glorpen.di.container.Container
.
Usage examples¶
Injecting services and parameters¶
from glorpen.di import Container
class MyParamService(object):
pass
class MyService(object):
def __init__(self, obj, text, value):
super(MyService, self).__init__()
print("service instance: %s" % obj)
print("container parameter: %s" % text)
print("provided value: %s" % value)
c = Container()
c.add_service(MyParamService)
c.add_parameter('my-param', "value from container")
c.add_service(MyService).kwargs(obj__svc=MyParamService, text__param="my-param", value="defined value")
c.get(MyService)
Running snippet will print:
service instance: <__main__.MyParamService object at 0x7f2fef6e9828>
container parameter: value from container
provided value: defined value
Arguments¶
In cases when you want to use inject parameter already used by internal methods, eg. glorpen.di.container.Service.call()
,
you can pass args with glorpen.di.container.Kwargs
helper class.
svc.configurator(callable=configurator, kwargs=Kwargs(router__svc=Router), other_param="param")
svc.call("some_method", kwargs=Kwargs(router__svc=Router), other_param="param")
Arguments defined by glorpen.di.container.Kwargs
are overriding ones provided by **kwargs notation.
Configurators¶
Configurators are services or callables used to configure main service. Provided callables are given main service instance as first argument.
def configurator(obj, some_service):
obj.some_thing = some_service()
svc.configurator(callable=configurator, some_service__svc=MyClass)
svc.configurator(service=ConfiguratorClass, method="some_method", some_service__svc=MyClass)
Factories¶
Services that create other objects. It is possible to provide parameters/other services from Container to given callables.
def factory(some_service):
return some_service("arg1")
svc1.factory(callable=factory, some_service__svc=MyClass)
class FactoryClass(object):
def create_new(self, some_service):
return some_service("arg1")
svc2.factory(service=FactoryClass, method="create_new", some_service__svc=MyClass)
Calling methods and settings properties¶
To call method on service creation:
svc.call("some_method", some_service__svc=MyClass)
To set properties on service creation:
svc.set(my_prop__svc=MyClass)
Using type hints for auto injection¶
Sometimes it is easier to just auto-fill function arguments, when using Python3 it can be done by arguments type hinting (see typing
for more information).
You can enable function hints lookup by using glorpen.di.container.Service.kwargs_from_signature()
for constructor arguments
and glorpen.di.container.Service.call_with_signature()
for methods.
from glorpen.di import Container
class MyParamService(object):
pass
class MyService(object):
def __init__(self, param:MyParamService):
super(MyService, self).__init__()
print("MyService.__init__: %s" % param.__class__.__name__)
def some_method(self, param:MyParamService):
print("MyService.some_method: %s" % param.__class__.__name__)
c = Container()
c.add_service(MyParamService)
c.add_service(MyService).kwargs_from_signature().call_with_signature("some_method")
print("Continer.get: %s" % c.get(MyService).__class__.__name__)
Snippet will create following output:
MyService.__init__: MyParamService
MyService.some_method: MyParamService
Continer.get: MyService
Adding custom scope¶
You can define new scope by extending glorpen.di.scopes.ScopeBase
and using glorpen.di.container.Container.set_scope_hierarchy()
.
from glorpen.di.scopes import ScopePrototype, ScopeSingleton, ScopeBase
from random import randint
class RandomScope(ScopeBase):
"""Returns new or cached instances based on random factor."""
def __init__(self, randomity=3):
super(RandomScope, self).__init__()
self.rnd = randomity
self.instances = {}
def get(self, creator, name):
if not name in self.instances or randint(0, self.rnd) == 0:
self.instances[name] = creator()
return self.instances[name]
c = Container()
# add scope with parameter
c.set_scope_hierarchy(ScopeSingleton, RandomScope(5), ScopePrototype)
# configure "str" service so we can see instances count
counter = 0
def configurator(kwargs):
global counter
kwargs.setdefault("object", "instance number: %d" % counter)
counter+=1
c.add_service('arg.test').implementation(str)\
.configurator(args_callable=configurator)\
.scope(RandomScope)
for i in range(0,10):
print(c.get("arg.test"))
Running script will print:
instance number: 0
instance number: 0
instance number: 0
instance number: 0
instance number: 1
instance number: 2
instance number: 2
instance number: 3
instance number: 4
instance number: 4