mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 09:36:48 +03:00
Add support of positional args for Factory & Singleton providers
This commit is contained in:
parent
2a5ed703bf
commit
824ed4f3e9
|
@ -18,6 +18,7 @@ from .providers import Callable
|
||||||
from .providers import Config
|
from .providers import Config
|
||||||
|
|
||||||
from .injections import Injection
|
from .injections import Injection
|
||||||
|
from .injections import Arg
|
||||||
from .injections import KwArg
|
from .injections import KwArg
|
||||||
from .injections import Attribute
|
from .injections import Attribute
|
||||||
from .injections import Method
|
from .injections import Method
|
||||||
|
@ -27,6 +28,7 @@ from .utils import is_provider
|
||||||
from .utils import ensure_is_provider
|
from .utils import ensure_is_provider
|
||||||
from .utils import is_injection
|
from .utils import is_injection
|
||||||
from .utils import ensure_is_injection
|
from .utils import ensure_is_injection
|
||||||
|
from .utils import is_arg_injection
|
||||||
from .utils import is_kwarg_injection
|
from .utils import is_kwarg_injection
|
||||||
from .utils import is_attribute_injection
|
from .utils import is_attribute_injection
|
||||||
from .utils import is_method_injection
|
from .utils import is_method_injection
|
||||||
|
@ -59,6 +61,7 @@ __all__ = (
|
||||||
|
|
||||||
# Injections
|
# Injections
|
||||||
'Injection',
|
'Injection',
|
||||||
|
'Arg',
|
||||||
'KwArg',
|
'KwArg',
|
||||||
'Attribute',
|
'Attribute',
|
||||||
'Method',
|
'Method',
|
||||||
|
@ -69,6 +72,7 @@ __all__ = (
|
||||||
'ensure_is_provider',
|
'ensure_is_provider',
|
||||||
'is_injection',
|
'is_injection',
|
||||||
'ensure_is_injection',
|
'ensure_is_injection',
|
||||||
|
'is_arg_injection',
|
||||||
'is_kwarg_injection',
|
'is_kwarg_injection',
|
||||||
'is_attribute_injection',
|
'is_attribute_injection',
|
||||||
'is_method_injection',
|
'is_method_injection',
|
||||||
|
|
|
@ -21,11 +21,10 @@ class Injection(object):
|
||||||
"""Base injection class."""
|
"""Base injection class."""
|
||||||
|
|
||||||
__IS_INJECTION__ = True
|
__IS_INJECTION__ = True
|
||||||
__slots__ = ('name', 'injectable', 'is_provider')
|
__slots__ = ('injectable', 'is_provider')
|
||||||
|
|
||||||
def __init__(self, name, injectable):
|
def __init__(self, injectable):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
self.name = name
|
|
||||||
self.injectable = injectable
|
self.injectable = injectable
|
||||||
self.is_provider = is_provider(injectable)
|
self.is_provider = is_provider(injectable)
|
||||||
|
|
||||||
|
@ -37,19 +36,36 @@ class Injection(object):
|
||||||
return self.injectable
|
return self.injectable
|
||||||
|
|
||||||
|
|
||||||
class KwArg(Injection):
|
class NamedInjection(Injection):
|
||||||
|
"""Base class of named injections."""
|
||||||
|
|
||||||
|
__slots__ = ('name',)
|
||||||
|
|
||||||
|
def __init__(self, name, injectable):
|
||||||
|
"""Initializer."""
|
||||||
|
self.name = name
|
||||||
|
super(NamedInjection, self).__init__(injectable)
|
||||||
|
|
||||||
|
|
||||||
|
class Arg(Injection):
|
||||||
|
"""Positional argument injection."""
|
||||||
|
|
||||||
|
__IS_ARG_INJECTION__ = True
|
||||||
|
|
||||||
|
|
||||||
|
class KwArg(NamedInjection):
|
||||||
"""Keyword argument injection."""
|
"""Keyword argument injection."""
|
||||||
|
|
||||||
__IS_KWARG_INJECTION__ = True
|
__IS_KWARG_INJECTION__ = True
|
||||||
|
|
||||||
|
|
||||||
class Attribute(Injection):
|
class Attribute(NamedInjection):
|
||||||
"""Attribute injection."""
|
"""Attribute injection."""
|
||||||
|
|
||||||
__IS_ATTRIBUTE_INJECTION__ = True
|
__IS_ATTRIBUTE_INJECTION__ = True
|
||||||
|
|
||||||
|
|
||||||
class Method(Injection):
|
class Method(NamedInjection):
|
||||||
"""Method injection."""
|
"""Method injection."""
|
||||||
|
|
||||||
__IS_METHOD_INJECTION__ = True
|
__IS_METHOD_INJECTION__ = True
|
||||||
|
|
|
@ -2,12 +2,16 @@
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from .injections import Arg
|
||||||
from .injections import KwArg
|
from .injections import KwArg
|
||||||
|
|
||||||
from .utils import ensure_is_provider
|
from .utils import ensure_is_provider
|
||||||
|
from .utils import is_injection
|
||||||
|
from .utils import is_arg_injection
|
||||||
from .utils import is_kwarg_injection
|
from .utils import is_kwarg_injection
|
||||||
from .utils import is_attribute_injection
|
from .utils import is_attribute_injection
|
||||||
from .utils import is_method_injection
|
from .utils import is_method_injection
|
||||||
|
from .utils import get_injectable_args
|
||||||
from .utils import get_injectable_kwargs
|
from .utils import get_injectable_kwargs
|
||||||
from .utils import GLOBAL_LOCK
|
from .utils import GLOBAL_LOCK
|
||||||
|
|
||||||
|
@ -104,33 +108,35 @@ class Factory(Provider):
|
||||||
Factory provider creates new instance of specified class on every call.
|
Factory provider creates new instance of specified class on every call.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('provides', 'kwargs', 'attributes', 'methods')
|
__slots__ = ('provides', 'args', 'kwargs', 'attributes', 'methods')
|
||||||
|
|
||||||
def __init__(self, provides, *injections, **kwargs):
|
def __init__(self, provides, *args, **kwargs):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
if not callable(provides):
|
if not callable(provides):
|
||||||
raise Error('Factory provider expects to get callable, ' +
|
raise Error('Factory provider expects to get callable, ' +
|
||||||
'got {0} instead'.format(str(provides)))
|
'got {0} instead'.format(str(provides)))
|
||||||
self.provides = provides
|
self.provides = provides
|
||||||
|
self.args = tuple(Arg(arg) if not is_injection(arg) else arg
|
||||||
|
for arg in args
|
||||||
|
if not is_injection(arg) or is_arg_injection(arg))
|
||||||
self.kwargs = tuple(injection
|
self.kwargs = tuple(injection
|
||||||
for injection in injections
|
for injection in args
|
||||||
if is_kwarg_injection(injection))
|
if is_kwarg_injection(injection))
|
||||||
if kwargs:
|
if kwargs:
|
||||||
self.kwargs += tuple(KwArg(name, value)
|
self.kwargs += tuple(KwArg(name, value)
|
||||||
for name, value in six.iteritems(kwargs))
|
for name, value in six.iteritems(kwargs))
|
||||||
self.attributes = tuple(injection
|
self.attributes = tuple(injection
|
||||||
for injection in injections
|
for injection in args
|
||||||
if is_attribute_injection(injection))
|
if is_attribute_injection(injection))
|
||||||
self.methods = tuple(injection
|
self.methods = tuple(injection
|
||||||
for injection in injections
|
for injection in args
|
||||||
if is_method_injection(injection))
|
if is_method_injection(injection))
|
||||||
super(Factory, self).__init__()
|
super(Factory, self).__init__()
|
||||||
|
|
||||||
def _provide(self, *args, **kwargs):
|
def _provide(self, *args, **kwargs):
|
||||||
"""Return provided instance."""
|
"""Return provided instance."""
|
||||||
instance = self.provides(*args,
|
instance = self.provides(*get_injectable_args(args, self.args),
|
||||||
**get_injectable_kwargs(kwargs,
|
**get_injectable_kwargs(kwargs, self.kwargs))
|
||||||
self.kwargs))
|
|
||||||
for attribute in self.attributes:
|
for attribute in self.attributes:
|
||||||
setattr(instance, attribute.name, attribute.value)
|
setattr(instance, attribute.name, attribute.value)
|
||||||
for method in self.methods:
|
for method in self.methods:
|
||||||
|
@ -141,7 +147,7 @@ class Factory(Provider):
|
||||||
@property
|
@property
|
||||||
def injections(self):
|
def injections(self):
|
||||||
"""Return tuple of all injections."""
|
"""Return tuple of all injections."""
|
||||||
return self.kwargs + self.attributes + self.methods
|
return self.args + self.kwargs + self.attributes + self.methods
|
||||||
|
|
||||||
|
|
||||||
class Singleton(Provider):
|
class Singleton(Provider):
|
||||||
|
@ -152,10 +158,10 @@ class Singleton(Provider):
|
||||||
|
|
||||||
__slots__ = ('instance', 'factory')
|
__slots__ = ('instance', 'factory')
|
||||||
|
|
||||||
def __init__(self, provides, *injections, **kwargs):
|
def __init__(self, provides, *args, **kwargs):
|
||||||
"""Initializer."""
|
"""Initializer."""
|
||||||
self.instance = None
|
self.instance = None
|
||||||
self.factory = Factory(provides, *injections, **kwargs)
|
self.factory = Factory(provides, *args, **kwargs)
|
||||||
super(Singleton, self).__init__()
|
super(Singleton, self).__init__()
|
||||||
|
|
||||||
def _provide(self, *args, **kwargs):
|
def _provide(self, *args, **kwargs):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Utils module."""
|
"""Utils module."""
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
|
import itertools
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
@ -41,6 +42,12 @@ def ensure_is_injection(instance):
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
def is_arg_injection(instance):
|
||||||
|
"""Check if instance is positional argument injection instance."""
|
||||||
|
return (not isinstance(instance, six.class_types) and
|
||||||
|
getattr(instance, '__IS_ARG_INJECTION__', False) is True)
|
||||||
|
|
||||||
|
|
||||||
def is_kwarg_injection(instance):
|
def is_kwarg_injection(instance):
|
||||||
"""Check if instance is keyword argument injection instance."""
|
"""Check if instance is keyword argument injection instance."""
|
||||||
return (not isinstance(instance, six.class_types) and
|
return (not isinstance(instance, six.class_types) and
|
||||||
|
@ -82,9 +89,14 @@ def ensure_is_catalog_bundle(instance):
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
def get_injectable_kwargs(kwargs, injections):
|
def get_injectable_args(context_args, arg_injections):
|
||||||
"""Return dictionary of kwargs, patched with injections."""
|
"""Return tuple of positional args, patched with injections."""
|
||||||
init_kwargs = dict(((injection.name, injection.value)
|
return itertools.chain((arg.value for arg in arg_injections), context_args)
|
||||||
for injection in injections))
|
|
||||||
init_kwargs.update(kwargs)
|
|
||||||
return init_kwargs
|
def get_injectable_kwargs(context_kwargs, kwarg_injections):
|
||||||
|
"""Return dictionary of keyword args, patched with injections."""
|
||||||
|
kwargs = dict((kwarg.name, kwarg.value)
|
||||||
|
for kwarg in kwarg_injections)
|
||||||
|
kwargs.update(context_kwargs)
|
||||||
|
return kwargs
|
||||||
|
|
|
@ -9,18 +9,17 @@ class InjectionTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
"""Test Injection creation and initialization."""
|
"""Test Injection creation and initialization."""
|
||||||
injection = di.Injection('some_arg_name', 'some_value')
|
injection = di.Injection('some_value')
|
||||||
self.assertEqual(injection.name, 'some_arg_name')
|
|
||||||
self.assertEqual(injection.injectable, 'some_value')
|
self.assertEqual(injection.injectable, 'some_value')
|
||||||
|
|
||||||
def test_value_with_scalar_injectable(self):
|
def test_value_with_scalar_injectable(self):
|
||||||
"""Test Injection value property with scalar value."""
|
"""Test Injection value property with scalar value."""
|
||||||
injection = di.Injection('some_arg_name', 'some_value')
|
injection = di.Injection('some_value')
|
||||||
self.assertEqual(injection.value, 'some_value')
|
self.assertEqual(injection.value, 'some_value')
|
||||||
|
|
||||||
def test_value_with_provider_injectable(self):
|
def test_value_with_provider_injectable(self):
|
||||||
"""Test Injection value property with provider."""
|
"""Test Injection value property with provider."""
|
||||||
injection = di.Injection('some_arg_name', di.Factory(object))
|
injection = di.Injection(di.Factory(object))
|
||||||
self.assertIsInstance(injection.value, object)
|
self.assertIsInstance(injection.value, object)
|
||||||
|
|
||||||
def test_value_with_catalog_bundle_injectable(self):
|
def test_value_with_catalog_bundle_injectable(self):
|
||||||
|
@ -35,6 +34,15 @@ class InjectionTests(unittest.TestCase):
|
||||||
self.assertIsInstance(injection.value, TestCatalog.Bundle)
|
self.assertIsInstance(injection.value, TestCatalog.Bundle)
|
||||||
|
|
||||||
|
|
||||||
|
class ArgTests(unittest.TestCase):
|
||||||
|
"""Positional arg injection test cases."""
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
"""Test Arg creation and initialization."""
|
||||||
|
injection = di.Arg('some_value')
|
||||||
|
self.assertEqual(injection.injectable, 'some_value')
|
||||||
|
|
||||||
|
|
||||||
class KwArgTests(unittest.TestCase):
|
class KwArgTests(unittest.TestCase):
|
||||||
"""Keyword arg injection test cases."""
|
"""Keyword arg injection test cases."""
|
||||||
|
|
||||||
|
|
|
@ -181,14 +181,12 @@ class FactoryTests(unittest.TestCase):
|
||||||
self.assertIsInstance(instance1, self.Example)
|
self.assertIsInstance(instance1, self.Example)
|
||||||
self.assertIsInstance(instance2, self.Example)
|
self.assertIsInstance(instance2, self.Example)
|
||||||
|
|
||||||
def test_call_with_init_args_simplified_syntax(self):
|
def test_call_with_init_positional_args(self):
|
||||||
"""Test creation of new instances with init args injections.
|
"""Test creation of new instances with init positional args.
|
||||||
|
|
||||||
New simplified syntax.
|
New simplified syntax.
|
||||||
"""
|
"""
|
||||||
provider = di.Factory(self.Example,
|
provider = di.Factory(self.Example, 'i1', 'i2')
|
||||||
init_arg1='i1',
|
|
||||||
init_arg2='i2')
|
|
||||||
|
|
||||||
instance1 = provider()
|
instance1 = provider()
|
||||||
instance2 = provider()
|
instance2 = provider()
|
||||||
|
@ -203,10 +201,53 @@ class FactoryTests(unittest.TestCase):
|
||||||
self.assertIsInstance(instance1, self.Example)
|
self.assertIsInstance(instance1, self.Example)
|
||||||
self.assertIsInstance(instance2, self.Example)
|
self.assertIsInstance(instance2, self.Example)
|
||||||
|
|
||||||
def test_call_with_init_args_old_syntax(self):
|
def test_call_with_init_keyword_args(self):
|
||||||
"""Test creation of new instances with init args injections."""
|
"""Test creation of new instances with init keyword args.
|
||||||
|
|
||||||
|
New simplified syntax.
|
||||||
|
"""
|
||||||
|
provider = di.Factory(self.Example, init_arg1='i1', init_arg2='i2')
|
||||||
|
|
||||||
|
instance1 = provider()
|
||||||
|
instance2 = provider()
|
||||||
|
|
||||||
|
self.assertEqual(instance1.init_arg1, 'i1')
|
||||||
|
self.assertEqual(instance1.init_arg2, 'i2')
|
||||||
|
|
||||||
|
self.assertEqual(instance2.init_arg1, 'i1')
|
||||||
|
self.assertEqual(instance2.init_arg2, 'i2')
|
||||||
|
|
||||||
|
self.assertIsNot(instance1, instance2)
|
||||||
|
self.assertIsInstance(instance1, self.Example)
|
||||||
|
self.assertIsInstance(instance2, self.Example)
|
||||||
|
|
||||||
|
def test_call_with_init_positional_and_keyword_args(self):
|
||||||
|
"""Test creation of new instances with init positional and keyword args.
|
||||||
|
|
||||||
|
Simplified syntax of positional and keyword arg injections.
|
||||||
|
"""
|
||||||
|
provider = di.Factory(self.Example,'i1', init_arg2='i2')
|
||||||
|
|
||||||
|
instance1 = provider()
|
||||||
|
instance2 = provider()
|
||||||
|
|
||||||
|
self.assertEqual(instance1.init_arg1, 'i1')
|
||||||
|
self.assertEqual(instance1.init_arg2, 'i2')
|
||||||
|
|
||||||
|
self.assertEqual(instance2.init_arg1, 'i1')
|
||||||
|
self.assertEqual(instance2.init_arg2, 'i2')
|
||||||
|
|
||||||
|
self.assertIsNot(instance1, instance2)
|
||||||
|
self.assertIsInstance(instance1, self.Example)
|
||||||
|
self.assertIsInstance(instance2, self.Example)
|
||||||
|
|
||||||
|
def test_call_with_init_positional_and_keyword_args_extended_syntax(self):
|
||||||
|
"""Test creation of new instances with init positional and keyword args.
|
||||||
|
|
||||||
|
Extended syntax of positional and keyword arg injections.
|
||||||
|
"""
|
||||||
provider = di.Factory(self.Example,
|
provider = di.Factory(self.Example,
|
||||||
di.KwArg('init_arg1', 'i1'),
|
di.Arg('i1'),
|
||||||
di.KwArg('init_arg2', 'i2'))
|
di.KwArg('init_arg2', 'i2'))
|
||||||
|
|
||||||
instance1 = provider()
|
instance1 = provider()
|
||||||
|
@ -300,7 +341,7 @@ class FactoryTests(unittest.TestCase):
|
||||||
def test_injections(self):
|
def test_injections(self):
|
||||||
"""Test getting a full list of injections using Factory.injections."""
|
"""Test getting a full list of injections using Factory.injections."""
|
||||||
provider = di.Factory(self.Example,
|
provider = di.Factory(self.Example,
|
||||||
di.KwArg('init_arg1', 1),
|
di.Arg(1),
|
||||||
di.KwArg('init_arg2', 2),
|
di.KwArg('init_arg2', 2),
|
||||||
di.Attribute('attribute1', 3),
|
di.Attribute('attribute1', 3),
|
||||||
di.Attribute('attribute2', 4),
|
di.Attribute('attribute2', 4),
|
||||||
|
|
|
@ -68,10 +68,11 @@ class IsInjectionTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_with_instance(self):
|
def test_with_instance(self):
|
||||||
"""Test with instance."""
|
"""Test with instance."""
|
||||||
self.assertTrue(di.is_injection(di.Injection('name', 'value')))
|
self.assertTrue(di.is_injection(di.Injection('value')))
|
||||||
|
|
||||||
def test_with_subclass_instances(self):
|
def test_with_subclass_instances(self):
|
||||||
"""Test with subclass instances."""
|
"""Test with subclass instances."""
|
||||||
|
self.assertTrue(di.is_injection(di.Arg('value')))
|
||||||
self.assertTrue(di.is_injection(di.KwArg('name', 'value')))
|
self.assertTrue(di.is_injection(di.KwArg('name', 'value')))
|
||||||
self.assertTrue(di.is_injection(di.Attribute('name', 'value')))
|
self.assertTrue(di.is_injection(di.Attribute('name', 'value')))
|
||||||
self.assertTrue(di.is_injection(di.Method('name', 'value')))
|
self.assertTrue(di.is_injection(di.Method('name', 'value')))
|
||||||
|
@ -94,7 +95,7 @@ class EnsureIsInjectionTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_with_instance(self):
|
def test_with_instance(self):
|
||||||
"""Test with instance."""
|
"""Test with instance."""
|
||||||
injection = di.Injection('name', 'value')
|
injection = di.Injection('value')
|
||||||
self.assertIs(di.ensure_is_injection(injection), injection)
|
self.assertIs(di.ensure_is_injection(injection), injection)
|
||||||
|
|
||||||
def test_with_class(self):
|
def test_with_class(self):
|
||||||
|
@ -110,6 +111,30 @@ class EnsureIsInjectionTests(unittest.TestCase):
|
||||||
self.assertRaises(di.Error, di.ensure_is_injection, object())
|
self.assertRaises(di.Error, di.ensure_is_injection, object())
|
||||||
|
|
||||||
|
|
||||||
|
class IsArgInjectionTests(unittest.TestCase):
|
||||||
|
"""`is_arg_injection()` test cases."""
|
||||||
|
|
||||||
|
def test_with_instance(self):
|
||||||
|
"""Test with instance."""
|
||||||
|
self.assertTrue(di.is_arg_injection(di.Arg('value')))
|
||||||
|
|
||||||
|
def test_with_class(self):
|
||||||
|
"""Test with class."""
|
||||||
|
self.assertFalse(di.is_arg_injection(di.Arg))
|
||||||
|
|
||||||
|
def test_with_parent_class(self):
|
||||||
|
"""Test with parent class."""
|
||||||
|
self.assertFalse(di.is_arg_injection(di.Injection))
|
||||||
|
|
||||||
|
def test_with_string(self):
|
||||||
|
"""Test with string."""
|
||||||
|
self.assertFalse(di.is_arg_injection('some_string'))
|
||||||
|
|
||||||
|
def test_with_object(self):
|
||||||
|
"""Test with object."""
|
||||||
|
self.assertFalse(di.is_arg_injection(object()))
|
||||||
|
|
||||||
|
|
||||||
class IsKwArgInjectionTests(unittest.TestCase):
|
class IsKwArgInjectionTests(unittest.TestCase):
|
||||||
"""`is_kwarg_injection()` test cases."""
|
"""`is_kwarg_injection()` test cases."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user