Skip to content

Instantly share code, notes, and snippets.

Created October 22, 2014 17:56
Show Gist options
  • Save asvetlov/1a24f6f4a63c6c2f1932 to your computer and use it in GitHub Desktop.
Save asvetlov/1a24f6f4a63c6c2f1932 to your computer and use it in GitHub Desktop.
Dependency Injection example
from inspect import getattr_static
class dep:
name = None
def __init__(self, type):
self.type = type
def __get__(self, instance, owner):
if is None:
raise RuntimeError("component was not initialized, use @component "
"decorator for {!r}".format(owner))
if instance is None:
return self
return instance.__dict__[]
except KeyError:
raise AttributeError("dep {!r} [{}] is not initialized".format(, self.type))
def __set__(self, instance, value):
raise TypeError("Cannot overwrite dependency")
def __delete__(self, instance):
raise TypeError("Cannot delete dependency")
def __repr__(self):
return '<Dep {!r} [{}]>'.format(, self.type)
def component(cls):
deps = []
for k in dir(cls):
v = getattr_static(cls, k)
if isinstance(v, dep):
# assert is None, = k
cls.__di_deps__ = tuple(deps)
return cls
class DI:
"""Base class for components with dependencies.
Usage example:
>>> @component
... class A:
... a = dep(int)
... b = dep(str)
>>> DI(A).deps
(<Dep 'a' [<class 'int'>]>, <Dep 'b' [<class 'str'>]>)
>>> a = A()
>>> DI(a).setup(a=1, b='val')
>>> a.a
>>> a.b
def __init__(self, inst):
self._inst = inst
self._deps = getattr(inst, '__di_deps__', None)
if self._deps is None:
raise TypeError("Argument is not component")
def deps(self):
"""Dependencies for component"""
return self._deps
def setup(self, **kwargs):
"""Initializes self dependencies.
**kwargs -- list of name->value pairs for dependencies to set up.
name is a name of dependency and value is a assigned value.
>>> @component
>>> class A:
... a = dep(int)
>>> a = A()
>>> DI(a).setup(a=1)
>>> a.a
Raises TypeError if value has a different type
than has been specified in dep(type) call.
Raises AttributeError if name doesn't point to any dependency.
marker = object()
to_set = {}
for d in self._deps:
v = kwargs.pop(, marker)
if v is not marker:
if not isinstance(v, d.type):
raise TypeError("Invalid type for {}, got {}"
.format(d, type(v)))
to_set[] = v
if kwargs:
raise AttributeError("dependencies {} are not found"
.format(', '.join(sorted(kwargs))))
# Assign new values only after all checks passed
def inject(self, component, *, ready=True):
"""Injects self dependencies into component.
component -- a component for injecting self dependencies into.
ready -- do self.ready() check before injecting (yes by default).
cdi = DI(component)
self_dct = self._inst.__dict__
cdi_dct = cdi._inst.__dict__
if ready:
marker = object()
vals = { self_dct[] for d in self._deps
if in self_dct}
to_set = {}
for d in cdi._deps:
name =
selfval = vals.get(name, marker)
if selfval is marker:
continue # nothing to assign
otherval = cdi_dct.get(name, marker)
if otherval is marker:
if not isinstance(selfval, d.type):
raise TypeError("Invalid type for {}: {!r}"
.format(d, selfval))
to_set[] = selfval
if selfval is not otherval:
raise TypeError("Overrriding {} with {!r}"
.format(d, selfval))
for k, v in to_set.items():
cdi_dct[k] = v
return component
def ready(self):
"""Check self for all dependencies has been initialized."""
missed = { for d in self._deps
if not in self._inst.__dict__}
if missed:
raise ValueError("Not-initialized attributes {}"
.format(', '.join(sorted(missed))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment