SunnyDI api reference

Inversion of Control

Framework for configuring and composing object graphs injecting their associated dependencies.

Using inversion-of-control rather than manually building object graphs can reduce an application’s maintenance burden.

For the philosophical reasoning behind such an architecture, see Martin Fowler’s [article](http://martinfowler.com/articles/injection.html).

Getting Started

In order to create an injector, we must first create and configure a sunnydi.ioc.Module. A module defines how instances will be built and provided to other instances in the object graph.

For our example, we will create a module for our HelloService.

#!python class HelloService(object):

def hello(self):
return ‘hello’

Now, we create a custom configuration module that extends sunnydi.ioc.Module. In the most simple configuration, we just bind a contract name to our HelloService class type:

#!python class HelloModule(Module):

def configure(self):
self.bind(‘hello_service’)
.to(HelloService)

We can then create an injector and resolve our HelloService like this:

#!python hello_module = HelloModule() injector = hello_module.create_injector() hello_service = injector.get(‘hello_service’)

>>> hello_service.hello()
'hello'

Advanced Configuration

More often than not, classes will have dependencies on other classes, and those classes will have additional dependencies. This results in potentially large object graphs that becomes very difficult to manage manually. On top of that, we probably only need to create some classes once for the lifetime of the application.

The below configuration illustrates how to accomplish this with the IoC configuration Module:

#!python class GoodbyeService(object):

# param name matches our binding contract name def __init__(self, hello_service):

self._hello_service = hello_service
def goodbye(self):
return ‘%s, goodbye’ % self._hello_service.hello()

class HelloModule(Module):

def configure(self):

# we only ever need one instance of this service self.bind(‘hello_service’)

.to(HelloService) .as_singleton()

# we only ever need one instance of this service self.bind(‘goodbye_service’)

.to(GoodbyeService) .as_singleton()

...

hello_module = HelloModule() injector = hello_module.create_injector()

# resolving the service multiple times # returns the same instance due to as_singleton() goodbye_service = injector.get(‘goodbye_service’) goodbye_service2 = injector.get(‘goodbye_service’)

>>> assert goodbye_service == goodbye_service2
True
>>> goodbye_service.goodbye()
'hello, goodbye'

Occasionally, manual configuration of a class is necessary in whole or in part. In these cases, the module can configure a factory method to provide the instance, or provide an instance as-is.

#!python class HelloModule(Module):

def configure(self):

# new up an instance on our own # this instance is de facto singleton hello_service = HelloService() # additional configuration ... self.bind(‘hello_service’)

.to_instance(hello_service)

# this service uses a factory to create the instance # factory can be static, instance, or global function # factory can also be marked as singleton self.bind(‘goodbye_service’)

.to_factory(self._create_goodbye_service) .as_singleton()

@staticmethod def _create_goodbye_service(hello_service):

goodbye_service = GoodbyeService(hello_service) # additional configuration ... return goodbye_service

Resolving Instances

Class instances can be resolved directly from the injector via their contract name(s) or class type(s). Multiple contracts may be resolved by adding additional parameters to the get() call.

#!python

# get one goodbye_service = injector.get(‘goodbye_service’)

# get many (hello_service, goodbye_service) = injector.get(‘hello_service’, ‘goodbye_service’)

# get can also take a class type goodbye_service = injector.get(GoodbyeService)

For CLI applications, resolving the main application class should be the only call to get() necessary (the remaining object graph should be populated via the injector).

For non-CLI or other applications in which object lifecycle isn’t fully controlled, the sunnydi.ioc.inject decorator may be used on a class’s __init__() method (MyClass does _not_ need to be configured in the module).

The sunnydi.ioc.inject decorator is not necessary for classes resolved via the injector (only for classes outside the injection context).

#!python class MyClass(object):

@inject def __init__(self, hello_service, goodbye_service):

self._hello_service = hello_service self._goodbye_service = goodbye_service

# same as calling my_class = injector.get(MyClass)

Global Injector

In some cases, there may be a need to import and use the sunnydi.ioc.Injector from the global context.

#!python from sunnydi.ioc import get_injector

The sunnydi.ioc.Injector can also be resolved from the sunnydi.ioc.inject decorator:

#!python @inject def __init__(self, injector):

pass

# same as calling injector = injector.get(‘injector’)

In order to use the global sunnydi.ioc.get_injector or the sunnydi.ioc.inject decorator, we must register the configuration module:

#!python hello_module = HelloModule() injector = hello_module.create_injector() hello_module.register(injector)

or (if we don’t need the sunnydi.ioc.Injector instance right away):

#!python hello_module = HelloModule() hello_module.register()
exception sunnydi.ioc.DependencyResolutionException[source]

Raised when an item could not be resolved from an sunnydi.ioc.Injector.

exception sunnydi.ioc.ComponentNotRegisteredException[source]

Raised when a component binding was not registered with an sunnydi.ioc.Injector.

exception sunnydi.ioc.ScopeDisposedException[source]

Raised when an sunnydi.ioc.InjectionScope has been disposed, but a client has attempted to resolve a component from it.

class sunnydi.ioc.Module(name=None)[source]

Configuration module for defining dependency-injection bindings.

add_module(module)[source]

Add a configuration sub-module to this module.

  • module (sunnydi.ioc.Module): A sub-module to add.
bind(contract)[source]

Create a new binding with the specified contract name.

  • contract (basestring): The contract name to bind to.

A binding builder.

configure()[source]

Configure the IoC module, creating any necessary injection bindings.

create_injector()[source]

Create the dependency sunnydi.ioc.Injector using the configured bindings.

A configured sunnydi.ioc.Injector.

name

The module name (or DEFAULT_INJECTOR).

register(injector=None)[source]

Register the specified sunnydi.ioc.Injector with the global scope. If injector parameter is not specified, create a new sunnydi.ioc.Injector and register it.

  • injector (sunnydi.ioc.Injector):
    The injector to register or create and register a new sunnydi.ioc.Injector if None.
class sunnydi.ioc.Injector(bindings=None)[source]

Dependency injector used for resolving dependencies.

This class is typically not created explicitly, rather it is created by configuring a sunnydi.ioc.Module.

get(*args, **kwargs)[source]

Resolve an instance or instances of the specified contracts.

  • contract (basestring): One or more contract names to resolve.
  • class_type (type): One or more classes to resolve with
    constructor parameters injected.
  • class_args (tuple): (Optional) Collection of arguments to pass
    to a resolving class’s positional arguments (*args) instead of resolving parameters via the injector.
  • class_kwargs (dict): (Optional) Collection of key-word arguments
    to pass to a resolving class’s keyword arguments (*kwargs) instead of resolving parameters via the injector.

The resolved object instance or tuple of instances if multiple parameters specified.

  • sunnydi.ioc.DependencyResolutionException:
    If no contracts or types are specified or if None type is specified.
  • sunnydi.ioc.ComponentNotRegisteredException:
    If a binding for the specified contract could not be found.
is_scope(scope_id)[source]

Whether or not a child sunnydi.ioc.InjectionScope with the specified scope id exists for this injector.

Will only return True for scopes created directly from this injector (not child scopes).

  • scope_id (basestring): The unique scope identifier.

True if a scope with the specified id exists, False if no scope exists.

scope(scope_id=None)[source]

Create a new child sunnydi.ioc.InjectionScope with the specified scope id or return a previously created child scope.

It’s recommended to use this method as a context manager in order to properly dispose of the scope when it’s finished being used.

#!python with injector.scope(‘my-scope-id’) as my_scope:

obj = my_scope.get(‘my_contract_name’)

If not being used as a context manager, it is mandatory to manually dispose of the scope via the sunnydi.ioc.InjectionScope.dispose() method when finished using the scope. Failure to do so will result in memory leaks within the application.

#!python my_scope = injector.scope(‘my-scope-id’) obj = my_scope.get(‘my_contract_name’) my_scope.dispose()
  • scope_id (basestring): (Optional) The unique scope identifier.
    If no scope id is specified, a random uuid.UUID is used to create a new scope id.

An sunnydi.ioc.InjectionScope

class sunnydi.ioc.InjectionScope(scope_id, bindings, parent_scope)[source]

Dependency injector used for resolving dependencies within a limited scope.

This class is typically not created explicitly, rather it is created from a parent sunnydi.ioc.Injector by calling scope().

dispose()[source]
get(*args, **kwargs)[source]

Resolve an instance or instances of the specified contracts within the specified scope.

  • contract (basestring): One or more contract names to resolve.
  • class_type (type): One or more classes to resolve with
    constructor parameters injected.
  • class_args (tuple): (Optional) Collection of arguments to pass
    to a resolving class’s positional arguments (*args) instead of resolving parameters via the injector.
  • class_kwargs (dict): (Optional) Collection of key-word arguments
    to pass to a resolving class’s keyword arguments (*kwargs) instead of resolving parameters via the injector.

The resolved object instance or tuple of instances if multiple parameters specified.

  • sunnydi.ioc.DependencyResolutionException:
    If no contracts or types are specified or if None type is specified.
  • sunnydi.ioc.ComponentNotRegisteredException:
    If a binding for the specified contract could not be found.
  • sunnydi.ioc.ScopeDisposedException:
    If the scope has been disposed.
scope_id
sunnydi.ioc.inject(f)[source]

Inject the dependencies for the decorated function.

Each of the function’s parameter names should match a configured binding name.

If a parameter is included directly, injection is skipped for that parameter.

sunnydi.ioc.get(*args, **kwargs)[source]

Resolve an instance or instances of the specified contracts.

  • contract (basestring): One or more contract names to resolve.
  • class_type (type): One or more classes to resolve with
    constructor parameters injected.
  • class_args (tuple): (Optional) Collection of arguments to pass
    to a resolving class’s positional arguments (*args) instead of resolving parameters via the injector.
  • class_kwargs (dict): (Optional) Collection of key-word arguments
    to pass to a resolving class’s keyword arguments (*kwargs) instead of resolving parameters via the injector.

The resolved object instance or tuple of instances if multiple parameters specified.

Raises:

  • sunnydi.ioc.DependencyResolutionException:
    If no contracts or types are specified or if None type is specified.
  • sunnydi.ioc.ComponentNotRegisteredException:
    If a binding for the specified contract could not be found.
sunnydi.ioc.scope(scope_id=None)[source]

Create a new child sunnydi.ioc.InjectionScope from the default injector, with the specified scope id or return a previously created child scope.

It’s recommended to use this method as a context manager in order to properly dispose of the scope when it’s finished being used.

#!python import ioc with ioc.scope(‘my-scope-id’) as my_scope:

obj = my_scope.get(‘my_contract_name’)

If not being used as a context manager, it is mandatory to manually dispose of the scope via the sunnydi.ioc.InjectionScope.dispose() method when finished using the scope. Failure to do so will result in memory leaks within the application.

#!python my_scope = ioc.scope(‘my-scope-id’) obj = my_scope.get(‘my_contract_name’) my_scope.dispose()
  • scope_id (basestring): (Optional) The unique scope identifier.
    If no scope id is specified, a random uuid.UUID is used to create a new scope id.

An sunnydi.ioc.InjectionScope

sunnydi.ioc.get_injector(name='Default')[source]

Get the sunnydi.ioc.Injector registered with the specified name, the default injector if no name is specified, or None if no injector is globally registered.

  • name (basestring): The injector name (or DEFAULT_INJECTOR).

The named sunnydi.ioc.Injector (or the default injector if name is not specified)