Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/2.0-injection-containers' into 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
rmk135 committed May 29, 2016
2 parents a1da758 + 99b6e27 commit 7e0a3c4
Show file tree
Hide file tree
Showing 15 changed files with 287 additions and 802 deletions.
32 changes: 16 additions & 16 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,26 +134,26 @@ IoC containers from previous example could look like these:
"""Catalog of platform service providers."""
database = providers.Singleton(sqlite3.connect) \
.args(':memory:')
.add_args(':memory:')
s3 = providers.Singleton(boto.s3.connection.S3Connection) \
.kwargs(aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
.add_kwargs(aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
class Services(catalogs.DeclarativeCatalog):
"""Catalog of business service providers."""
users = providers.Factory(example.services.Users) \
.kwargs(db=Platform.database)
.add_kwargs(db=Platform.database)
photos = providers.Factory(example.services.Photos) \
.kwargs(db=Platform.database,
s3=Platform.s3)
.add_kwargs(db=Platform.database,
s3=Platform.s3)
auth = providers.Factory(example.services.Auth) \
.kwargs(db=Platform.database,
token_ttl=3600)
.add_kwargs(db=Platform.database,
token_ttl=3600)
or like this these:

Expand All @@ -163,26 +163,26 @@ or like this these:
"""Catalog of platform service providers."""
database = providers.Singleton(sqlite3.connect)
database.args(':memory:')
database.add_args(':memory:')
s3 = providers.Singleton(boto.s3.connection.S3Connection)
s3.kwargs(aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
s3.add_kwargs(aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
class Services(catalogs.DeclarativeCatalog):
"""Catalog of business service providers."""
users = providers.Factory(example.services.Users)
users.kwargs(db=Platform.database)
users.add_kwargs(db=Platform.database)
photos = providers.Factory(example.services.Photos)
photos.kwargs(db=Platform.database,
s3=Platform.s3)
photos.add_kwargs(db=Platform.database,
s3=Platform.s3)
auth = providers.Factory(example.services.Auth)
auth.kwargs(db=Platform.database,
token_ttl=3600)
auth.add_kwargs(db=Platform.database,
token_ttl=3600)
You can get more *Dependency Injector* examples in ``/examples`` directory on
GitHub:
Expand Down
193 changes: 17 additions & 176 deletions dependency_injector/injections.py
Original file line number Diff line number Diff line change
@@ -1,148 +1,13 @@
"""Injections module."""

import itertools

import six

from dependency_injector.utils import (
is_provider,
is_injection,
is_arg_injection,
is_kwarg_injection,
is_delegated_provider,
fetch_cls_init,
from dependency_injector.providers.base import (
_parse_positional_injections,
_parse_keyword_injections,
)

from dependency_injector.errors import Error


@six.python_2_unicode_compatible
class Injection(object):
"""Base injection class.
All injections extend this class.
.. py:attribute:: injectable
Injectable value, could be provider or any other object.
:type: object | :py:class:`dependency_injector.providers.Provider`
.. py:attribute:: call_injectable
Flag that is set to ``True`` if it is needed to call injectable.
Injectable needs to be called if it is not delegated provider.
:type: bool
"""

__IS_INJECTION__ = True
__slots__ = ('injectable', 'get_value')

def __init__(self, injectable):
"""Initializer.
:param injectable: Injectable value, could be provider or any
other object.
:type injectable: object |
:py:class:`dependency_injector.providers.Provider`
"""
self.injectable = injectable

if not is_provider(injectable) or is_delegated_provider(injectable):
def injectable():
return self.injectable
self.get_value = injectable

super(Injection, self).__init__()

def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return '<{injection}({injectable}) at {address}>'.format(
injection='.'.join((self.__class__.__module__,
self.__class__.__name__)),
injectable=repr(self.injectable),
address=hex(id(self)))

__repr__ = __str__


class Arg(Injection):
"""Positional argument injection."""

__IS_ARG_INJECTION__ = True


@six.python_2_unicode_compatible
class _NamedInjection(Injection):
"""Base class of named injections.
.. py:attribute:: name
Injection target's name (keyword argument, attribute).
:type: str
"""

__slots__ = ('name',)

def __init__(self, name, injectable):
"""Initializer.
:param name: Injection target's name.
:type name: str
:param injectable: Injectable value, could be provider or any
other object.
:type injectable: object |
:py:class:`dependency_injector.providers.Provider`
"""
self.name = name
super(_NamedInjection, self).__init__(injectable)

def __str__(self):
"""Return string representation of provider.
:rtype: str
"""
return '<{injection}({name}, {injectable}) at {address}>'.format(
name=repr(self.name),
injection='.'.join((self.__class__.__module__,
self.__class__.__name__)),
injectable=repr(self.injectable),
address=hex(id(self)))

__repr__ = __str__


class KwArg(_NamedInjection):
"""Keyword argument injection.
.. py:attribute:: name
Keyword argument's name.
:type: str
"""

__IS_KWARG_INJECTION__ = True


class Attribute(_NamedInjection):
"""Attribute injection.
.. py:attribute:: name
Attribute's name.
:type: str
"""

__IS_ATTRIBUTE_INJECTION__ = True
from dependency_injector import utils
from dependency_injector import errors


def inject(*args, **kwargs):
Expand All @@ -157,24 +22,18 @@ def inject(*args, **kwargs):
.. code-block:: python
# Positional arguments injections (simplified syntax):
# Positional arguments injections:
@inject(1, 2)
def some_function(arg1, arg2):
pass
# Keyword arguments injections (simplified syntax):
# Keyword arguments injections:
@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):
# Keyword arguments injections into class init:
@inject(arg1=1)
@inject(arg2=2)
class SomeClass(object):
Expand All @@ -191,16 +50,16 @@ def __init__(self, arg1, arg2):
:return: Class / callable decorator
:rtype: (callable) -> (type | callable)
"""
arg_injections = _parse_args_injections(args)
kwarg_injections = _parse_kwargs_injections(args, kwargs)
arg_injections = _parse_positional_injections(args)
kwarg_injections = _parse_keyword_injections(kwargs)

def decorator(callback_or_cls):
"""Dependency injection decorator."""
if isinstance(callback_or_cls, six.class_types):
cls = callback_or_cls
cls_init = fetch_cls_init(cls)
cls_init = utils.fetch_cls_init(cls)
if not cls_init:
raise Error(
raise errors.Error(
'Class {0}.{1} has no __init__() '.format(cls.__module__,
cls.__name__) +
'method and could not be decorated with @inject decorator')
Expand All @@ -211,43 +70,25 @@ def decorator(callback_or_cls):

if hasattr(callback, '__INJECT_DECORATED__'):
callback.args += arg_injections
callback.kwargs += kwarg_injections
callback.injections += arg_injections + kwarg_injections
callback.kwargs.update(kwarg_injections)
return callback

@six.wraps(callback)
def decorated(*args, **kwargs):
"""Decorated with dependency injection callback."""
if decorated.args:
args = tuple(arg.get_value() for arg in decorated.args) + args
args = tuple(arg.inject() for arg in decorated.args) + args

for kwarg in decorated.kwargs:
if kwarg.name not in kwargs:
kwargs[kwarg.name] = kwarg.get_value()
for name, arg in six.iteritems(decorated.kwargs):
if name not in kwargs:
kwargs[name] = arg.inject()

return callback(*args, **kwargs)

decorated.__INJECT_DECORATED__ = True
decorated.origin = callback
decorated.args = arg_injections
decorated.kwargs = kwarg_injections
decorated.injections = arg_injections + kwarg_injections

return decorated
return decorator


def _parse_args_injections(args):
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):
kwarg_injections = tuple(injection
for injection in args
if is_kwarg_injection(injection))
if kwargs:
kwarg_injections += tuple(itertools.starmap(KwArg,
six.iteritems(kwargs)))
return kwarg_injections
12 changes: 5 additions & 7 deletions dependency_injector/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
Delegate,
Object,
ExternalDependency,
OverridingContext,
override,
)
from dependency_injector.providers.callable import (
Callable,
Expand All @@ -16,10 +18,6 @@
Singleton,
DelegatedSingleton,
)
from dependency_injector.providers.utils import (
OverridingContext,
override,
)


__all__ = (
Expand All @@ -28,14 +26,14 @@
'Object',
'ExternalDependency',

'OverridingContext',
'override',

'Callable',
'DelegatedCallable',

'Factory',
'DelegatedFactory',
'Singleton',
'DelegatedSingleton',

'OverridingContext',
'override',
)

0 comments on commit 7e0a3c4

Please sign in to comment.