Merge remote-tracking branch 'origin/inject_decorator_positional_args'

This commit is contained in:
Roman Mogilatov 2015-10-23 15:04:33 +03:00
commit 9a9861e4b0
7 changed files with 131 additions and 25 deletions

View File

@ -77,10 +77,10 @@ class Method(NamedInjection):
def inject(*args, **kwargs): def inject(*args, **kwargs):
"""Dependency injection decorator. """Dependency injection decorator.
:type injection: Injection
:return: (callable) -> (callable) :return: (callable) -> (callable)
""" """
injections = _parse_kwargs_injections(args, kwargs) arg_injections = _parse_args_injections(args)
kwarg_injections = _parse_kwargs_injections(args, kwargs)
def decorator(callback_or_cls): def decorator(callback_or_cls):
"""Dependency injection decorator.""" """Dependency injection decorator."""
@ -99,17 +99,20 @@ def inject(*args, **kwargs):
callback = callback_or_cls callback = callback_or_cls
if hasattr(callback, 'injections'): if hasattr(callback, 'injections'):
callback.injections += injections callback.args += arg_injections
callback.kwargs += kwarg_injections
callback.injections += arg_injections + kwarg_injections
return callback return callback
@six.wraps(callback) @six.wraps(callback)
def decorated(*args, **kwargs): def decorated(*args, **kwargs):
"""Decorated with dependency injection callback.""" """Decorated with dependency injection callback."""
return callback(*args, return callback(*_get_injectable_args(args, decorated.args),
**_get_injectable_kwargs(kwargs, **_get_injectable_kwargs(kwargs, decorated.kwargs))
decorated.injections))
decorated.injections = injections decorated.args = arg_injections
decorated.kwargs = kwarg_injections
decorated.injections = arg_injections + kwarg_injections
return decorated return decorated
return decorator return decorator

View File

@ -11,10 +11,25 @@ Current section of documentation describes advanced usage of
injections. It *patches* decorated callable in such way that dependency injections. It *patches* decorated callable in such way that dependency
injection will be done during every call of decorated callable. injection will be done during every call of decorated callable.
``@di.inject()`` decorator takes keyword argument, that will be injected ``di.inject()`` takes a various number of positional and keyword arguments
during every next call of decorated callback with the same name. Any Python that are used as decorated callable injections. Every time, when
object will be injected *as is*, except ``di.Provider``'s, which will be ``di.inject()`` is called, positional and keyword argument injections would be
called to provide injectable values. passed as an callable arguments.
Such behaviour is very similar to the standard Python ``functools.partial``
object, except of one thing: all injectable values are provided
*"as is"*, except of providers (subclasses of ``di.Provider``). Providers
will be called every time, when injection needs to be done. For example,
if injectable value of injection is a ``di.Factory``, it will provide new one
instance (as a result of its call) every time, when injection needs to be done.
``di.inject()`` behaviour with context positional and keyword arguments is
very like a standard Python ``functools.partial``:
- Positional context arguments will be appended after ``di.inject()``
positional injections.
- Keyword context arguments have priority on ``di.inject()`` keyword
injections and will be merged over them.
Example: Example:

View File

@ -16,7 +16,8 @@ Development version
- Add images for catalog "Writing catalogs" and "Operating with catalogs" - Add images for catalog "Writing catalogs" and "Operating with catalogs"
examples. examples.
- Add functionality for using positional argument injections with - Add functionality for using positional argument injections with
``di.Factory``, ``di.Singleton`` and ``di.Callable`` providers. ``di.Factory``, ``di.Singleton``, ``di.Callable`` providers and
``di.inject`` decorator.
- Add functionality for decorating classes with ``@di.inject``. - Add functionality for decorating classes with ``@di.inject``.
- Add ``di.Singleton.injections`` attribute that represents a tuple of all - Add ``di.Singleton.injections`` attribute that represents a tuple of all
``di.Singleton`` injections (including args, kwargs, attributes and methods). ``di.Singleton`` injections (including args, kwargs, attributes and methods).

View File

@ -5,8 +5,8 @@ import flask
import dependency_injector as di import dependency_injector as di
database = di.Singleton(sqlite3.Connection, database = di.Singleton(sqlite3.connect,
database=':memory:', ':memory:',
timeout=30, timeout=30,
detect_types=True, detect_types=True,
isolation_level='EXCLUSIVE') isolation_level='EXCLUSIVE')
@ -15,7 +15,7 @@ app = flask.Flask(__name__)
@app.route('/') @app.route('/')
@di.inject(database=database) @di.inject(database)
def hello(database): def hello(database):
"""Example Flask view.""" """Example Flask view."""
one = database.execute('SELECT 1').fetchone()[0] one = database.execute('SELECT 1').fetchone()[0]

View File

@ -6,11 +6,22 @@ import dependency_injector as di
dependency_injector_factory = di.Factory(object) dependency_injector_factory = di.Factory(object)
# Example of using `di.inject()` decorator keyword argument injections:
@di.inject(new_object=dependency_injector_factory) @di.inject(new_object=dependency_injector_factory)
@di.inject(some_setting=1334) @di.inject(some_setting=1334)
def example_callback(new_object, some_setting): def example_callback1(new_object, some_setting):
"""Example callback that does some asserts for input args.""" """Example callback that does some asserts for input args."""
assert isinstance(new_object, object) assert isinstance(new_object, object)
assert some_setting == 1334 assert some_setting == 1334
example_callback()
# Example of using `di.inject()` decorator with positional argument injections:
@di.inject(dependency_injector_factory, 1334)
def example_callback2(new_object, some_setting):
"""Example callback that does some asserts for input args."""
assert isinstance(new_object, object)
assert some_setting == 1334
example_callback1()
example_callback2()

View File

@ -75,8 +75,89 @@ class MethodTests(unittest.TestCase):
class InjectTests(unittest.TestCase): class InjectTests(unittest.TestCase):
"""Inject decorator test cases.""" """Inject decorator test cases."""
def test_decorated(self): def test_decorated_args(self):
"""Test `inject()` decorated callback.""" """Test `inject()` decoration with args."""
provider1 = di.Factory(object)
provider2 = di.Factory(list)
@di.inject(provider1, provider2)
def test(a, b):
return a, b
a1, b1 = test()
a2, b2 = test()
self.assertIsInstance(a1, object)
self.assertIsInstance(a2, object)
self.assertIsNot(a1, a2)
self.assertIsInstance(b1, list)
self.assertIsInstance(b2, list)
self.assertIsNot(b1, b2)
def test_decorated_args_extended_syntax(self):
"""Test `inject()` decoration with args."""
provider1 = di.Factory(object)
provider2 = di.Factory(list)
@di.inject(di.Arg(provider1), di.Arg(provider2))
def test(a, b):
return a, b
a1, b1 = test()
a2, b2 = test()
self.assertIsInstance(a1, object)
self.assertIsInstance(a2, object)
self.assertIsNot(a1, a2)
self.assertIsInstance(b1, list)
self.assertIsInstance(b2, list)
self.assertIsNot(b1, b2)
def test_decorated_args_several_times(self):
"""Test `inject()` decoration with args several times."""
provider1 = di.Factory(object)
provider2 = di.Factory(list)
@di.inject(provider2)
@di.inject(provider1)
def test(a, b):
return a, b
a1, b1 = test()
a2, b2 = test()
self.assertIsInstance(a1, object)
self.assertIsInstance(a2, object)
self.assertIsNot(a1, a2)
self.assertIsInstance(b1, list)
self.assertIsInstance(b2, list)
self.assertIsNot(b1, b2)
def test_decorated_context_args(self):
"""Test `inject()` decoration with context args."""
provider1 = di.Factory(object)
provider2 = di.Factory(list)
@di.inject(provider1)
def test(a, b):
return a, b
a1, b1 = test(provider2())
a2, b2 = test(provider2())
self.assertIsInstance(a1, object)
self.assertIsInstance(a2, object)
self.assertIsNot(a1, a2)
self.assertIsInstance(b1, list)
self.assertIsInstance(b2, list)
self.assertIsNot(b1, b2)
def test_decorated_kwargs(self):
"""Test `inject()` decoration with kwargs."""
provider1 = di.Factory(object) provider1 = di.Factory(object)
provider2 = di.Factory(list) provider2 = di.Factory(list)

View File

@ -9,12 +9,7 @@ class Example(object):
def __init__(self, init_arg1=None, init_arg2=None, init_arg3=None, def __init__(self, init_arg1=None, init_arg2=None, init_arg3=None,
init_arg4=None): init_arg4=None):
"""Initializer. """Initializer."""
:param init_arg1:
:param init_arg2:
:return:
"""
self.init_arg1 = init_arg1 self.init_arg1 = init_arg1
self.init_arg2 = init_arg2 self.init_arg2 = init_arg2
self.init_arg3 = init_arg3 self.init_arg3 = init_arg3