2015-03-09 01:01:39 +03:00
|
|
|
"""Injections module."""
|
2015-01-10 12:24:25 +03:00
|
|
|
|
2015-09-28 18:45:15 +03:00
|
|
|
import sys
|
2015-10-21 11:44:25 +03:00
|
|
|
import itertools
|
|
|
|
|
2015-09-01 00:30:38 +03:00
|
|
|
import six
|
2015-08-03 12:57:42 +03:00
|
|
|
|
2015-03-11 16:18:42 +03:00
|
|
|
from .utils import is_provider
|
2015-10-21 11:44:25 +03:00
|
|
|
from .utils import is_injection
|
|
|
|
from .utils import is_arg_injection
|
|
|
|
from .utils import is_kwarg_injection
|
2015-03-11 16:18:42 +03:00
|
|
|
|
2015-09-28 14:19:22 +03:00
|
|
|
from .errors import Error
|
|
|
|
|
2015-01-10 12:24:25 +03:00
|
|
|
|
2015-09-28 18:45:15 +03:00
|
|
|
IS_PYPY = '__pypy__' in sys.builtin_module_names
|
|
|
|
if IS_PYPY or six.PY3: # pragma: no cover
|
|
|
|
OBJECT_INIT = six.get_unbound_function(object.__init__)
|
|
|
|
else: # pragma: no cover
|
|
|
|
OBJECT_INIT = None
|
|
|
|
|
2015-01-10 12:24:25 +03:00
|
|
|
|
|
|
|
class Injection(object):
|
2015-11-24 11:33:10 +03:00
|
|
|
"""Base injection class.
|
|
|
|
|
|
|
|
All injections extend this class.
|
|
|
|
"""
|
2015-01-10 12:24:25 +03:00
|
|
|
|
2015-07-22 10:53:16 +03:00
|
|
|
__IS_INJECTION__ = True
|
2015-10-14 14:30:01 +03:00
|
|
|
__slots__ = ('injectable', 'is_provider')
|
2015-03-11 16:18:42 +03:00
|
|
|
|
2015-10-14 14:30:01 +03:00
|
|
|
def __init__(self, injectable):
|
2015-11-24 11:33:10 +03:00
|
|
|
"""Initializer.
|
|
|
|
|
|
|
|
:param injectable: Injectable value, could be provider or any
|
|
|
|
other object.
|
|
|
|
:type injectable: object |
|
|
|
|
:py:class:`dependency_injector.providers.Provider`
|
|
|
|
"""
|
2015-01-10 12:24:25 +03:00
|
|
|
self.injectable = injectable
|
2015-11-24 11:33:10 +03:00
|
|
|
"""Injectable value, could be provider or any other object.
|
|
|
|
|
|
|
|
:type: object | :py:class:`dependency_injector.providers.Provider`
|
|
|
|
"""
|
|
|
|
|
2015-10-19 10:50:17 +03:00
|
|
|
self.is_provider = is_provider(injectable)
|
2015-11-24 11:33:10 +03:00
|
|
|
"""Flag that is set to ``True`` if injectable value is provider.
|
|
|
|
|
|
|
|
:type: bool
|
|
|
|
"""
|
|
|
|
|
|
|
|
super(Injection, self).__init__()
|
2015-01-10 12:24:25 +03:00
|
|
|
|
2015-01-11 16:03:45 +03:00
|
|
|
@property
|
|
|
|
def value(self):
|
2015-11-24 11:33:10 +03:00
|
|
|
"""Read-only property that represents injectable value.
|
|
|
|
|
|
|
|
Injectable values are provided "as is", except of providers
|
|
|
|
(subclasses of :py:class:`dependency_injector.providers.Provider`).
|
|
|
|
Providers will be called every time, when injection needs to be done.
|
|
|
|
|
|
|
|
:rtype: object
|
|
|
|
"""
|
2015-10-19 10:50:17 +03:00
|
|
|
if self.is_provider:
|
2015-01-11 16:03:45 +03:00
|
|
|
return self.injectable()
|
|
|
|
return self.injectable
|
2015-01-10 12:24:25 +03:00
|
|
|
|
|
|
|
|
2015-11-16 14:28:27 +03:00
|
|
|
class _NamedInjection(Injection):
|
2015-10-14 14:30:01 +03:00
|
|
|
"""Base class of named injections."""
|
|
|
|
|
|
|
|
__slots__ = ('name',)
|
|
|
|
|
|
|
|
def __init__(self, name, injectable):
|
|
|
|
"""Initializer."""
|
|
|
|
self.name = name
|
2015-11-16 14:28:27 +03:00
|
|
|
super(_NamedInjection, self).__init__(injectable)
|
2015-10-14 14:30:01 +03:00
|
|
|
|
|
|
|
|
|
|
|
class Arg(Injection):
|
|
|
|
"""Positional argument injection."""
|
|
|
|
|
|
|
|
__IS_ARG_INJECTION__ = True
|
|
|
|
|
|
|
|
|
2015-11-16 14:28:27 +03:00
|
|
|
class KwArg(_NamedInjection):
|
2015-03-23 02:04:18 +03:00
|
|
|
"""Keyword argument injection."""
|
2015-01-10 12:24:25 +03:00
|
|
|
|
2015-07-22 10:53:16 +03:00
|
|
|
__IS_KWARG_INJECTION__ = True
|
2015-03-11 16:18:42 +03:00
|
|
|
|
2015-01-10 12:24:25 +03:00
|
|
|
|
2015-11-16 14:28:27 +03:00
|
|
|
class Attribute(_NamedInjection):
|
2015-03-09 01:01:39 +03:00
|
|
|
"""Attribute injection."""
|
2015-01-10 12:24:25 +03:00
|
|
|
|
2015-07-22 10:53:16 +03:00
|
|
|
__IS_ATTRIBUTE_INJECTION__ = True
|
2015-03-11 16:18:42 +03:00
|
|
|
|
2015-01-10 12:24:25 +03:00
|
|
|
|
2015-11-16 14:28:27 +03:00
|
|
|
class Method(_NamedInjection):
|
2015-03-09 01:01:39 +03:00
|
|
|
"""Method injection."""
|
2015-03-11 16:18:42 +03:00
|
|
|
|
2015-07-22 10:53:16 +03:00
|
|
|
__IS_METHOD_INJECTION__ = True
|
2015-08-03 12:57:42 +03:00
|
|
|
|
|
|
|
|
2015-09-01 00:30:38 +03:00
|
|
|
def inject(*args, **kwargs):
|
2015-08-03 12:57:42 +03:00
|
|
|
"""Dependency injection decorator.
|
|
|
|
|
2015-11-24 11:33:10 +03:00
|
|
|
:py:func:`inject` decorator can be used for making inline dependency
|
|
|
|
injections. It patches decorated callable in such way that dependency
|
|
|
|
injection will be done during every call of decorated callable.
|
|
|
|
|
|
|
|
:py:func:`inject` decorator supports different syntaxes of passing
|
|
|
|
injections:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
# Positional arguments injections (simplified syntax):
|
|
|
|
@inject(1, 2)
|
|
|
|
def some_function(arg1, arg2):
|
|
|
|
pass
|
|
|
|
|
|
|
|
# Keyword arguments injections (simplified syntax):
|
|
|
|
@inject(arg1=1)
|
|
|
|
@inject(arg2=2)
|
|
|
|
def some_function(arg1, arg2):
|
|
|
|
pass
|
|
|
|
|
|
|
|
# Keyword arguments injections (extended (full) syntax):
|
|
|
|
@inject(KwArg('arg1', 1))
|
|
|
|
@inject(KwArg('arg2', 2))
|
|
|
|
def some_function(arg1, arg2):
|
|
|
|
pass
|
|
|
|
|
|
|
|
# Keyword arguments injections into class init (simplified syntax):
|
|
|
|
@inject(arg1=1)
|
|
|
|
@inject(arg2=2)
|
|
|
|
class SomeClass(object):
|
|
|
|
|
|
|
|
def __init__(self, arg1, arg2):
|
|
|
|
pass
|
|
|
|
|
|
|
|
:return: Class / callable decorator
|
|
|
|
:rtype: (callable) -> (type | callable)
|
2015-08-03 12:57:42 +03:00
|
|
|
"""
|
2015-10-23 09:53:53 +03:00
|
|
|
arg_injections = _parse_args_injections(args)
|
|
|
|
kwarg_injections = _parse_kwargs_injections(args, kwargs)
|
2015-08-03 12:57:42 +03:00
|
|
|
|
2015-09-28 22:10:17 +03:00
|
|
|
def decorator(callback_or_cls):
|
2015-08-03 12:57:42 +03:00
|
|
|
"""Dependency injection decorator."""
|
2015-09-28 22:10:17 +03:00
|
|
|
if isinstance(callback_or_cls, six.class_types):
|
|
|
|
cls = callback_or_cls
|
2015-09-28 14:19:22 +03:00
|
|
|
try:
|
2015-09-28 14:32:07 +03:00
|
|
|
cls_init = six.get_unbound_function(cls.__init__)
|
2015-09-28 18:45:15 +03:00
|
|
|
assert cls_init is not OBJECT_INIT
|
2015-09-28 14:32:07 +03:00
|
|
|
except (AttributeError, AssertionError):
|
2015-09-28 14:19:22 +03:00
|
|
|
raise Error(
|
2015-09-28 18:45:15 +03:00
|
|
|
'Class {0}.{1} has no __init__() '.format(cls.__module__,
|
|
|
|
cls.__name__) +
|
2015-09-28 14:19:22 +03:00
|
|
|
'method and could not be decorated with @inject decorator')
|
|
|
|
cls.__init__ = decorator(cls_init)
|
|
|
|
return cls
|
|
|
|
|
2015-09-28 22:10:17 +03:00
|
|
|
callback = callback_or_cls
|
2015-09-14 10:53:24 +03:00
|
|
|
if hasattr(callback, 'injections'):
|
2015-10-23 09:53:53 +03:00
|
|
|
callback.args += arg_injections
|
|
|
|
callback.kwargs += kwarg_injections
|
|
|
|
callback.injections += arg_injections + kwarg_injections
|
2015-09-01 00:30:38 +03:00
|
|
|
return callback
|
2015-08-03 12:57:42 +03:00
|
|
|
|
2015-09-01 00:30:38 +03:00
|
|
|
@six.wraps(callback)
|
2015-08-03 12:57:42 +03:00
|
|
|
def decorated(*args, **kwargs):
|
|
|
|
"""Decorated with dependency injection callback."""
|
2015-10-23 09:53:53 +03:00
|
|
|
return callback(*_get_injectable_args(args, decorated.args),
|
|
|
|
**_get_injectable_kwargs(kwargs, decorated.kwargs))
|
2015-08-03 12:57:42 +03:00
|
|
|
|
2015-10-23 09:53:53 +03:00
|
|
|
decorated.args = arg_injections
|
|
|
|
decorated.kwargs = kwarg_injections
|
|
|
|
decorated.injections = arg_injections + kwarg_injections
|
2015-08-03 12:57:42 +03:00
|
|
|
|
|
|
|
return decorated
|
|
|
|
return decorator
|
2015-10-21 11:44:25 +03:00
|
|
|
|
|
|
|
|
|
|
|
def _parse_args_injections(args):
|
|
|
|
"""Parse positional argument injections according to current syntax."""
|
|
|
|
return tuple(Arg(arg) if not is_injection(arg) else arg
|
|
|
|
for arg in args
|
|
|
|
if not is_injection(arg) or is_arg_injection(arg))
|
|
|
|
|
|
|
|
|
|
|
|
def _parse_kwargs_injections(args, kwargs):
|
|
|
|
"""Parse keyword argument injections according to current syntax."""
|
|
|
|
kwarg_injections = tuple(injection
|
|
|
|
for injection in args
|
|
|
|
if is_kwarg_injection(injection))
|
|
|
|
if kwargs:
|
|
|
|
kwarg_injections += tuple(KwArg(name, value)
|
|
|
|
for name, value in six.iteritems(kwargs))
|
|
|
|
return kwarg_injections
|
|
|
|
|
|
|
|
|
|
|
|
def _get_injectable_args(context_args, arg_injections):
|
|
|
|
"""Return tuple of positional arguments, patched with injections."""
|
|
|
|
return itertools.chain((arg.value for arg in arg_injections), context_args)
|
|
|
|
|
|
|
|
|
|
|
|
def _get_injectable_kwargs(context_kwargs, kwarg_injections):
|
|
|
|
"""Return dictionary of keyword arguments, patched with injections."""
|
|
|
|
injectable_kwargs = dict((kwarg.name, kwarg.value)
|
|
|
|
for kwarg in kwarg_injections)
|
|
|
|
injectable_kwargs.update(context_kwargs)
|
|
|
|
return injectable_kwargs
|