Merge remote-tracking branch 'origin/factory_and_singleton_positional_args'

This commit is contained in:
Roman Mogilatov 2015-10-19 17:31:42 +03:00
commit 402539ed7f
12 changed files with 472 additions and 122 deletions

View File

@ -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',

View File

@ -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

View File

@ -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):
@ -169,6 +175,11 @@ class Singleton(Provider):
"""Reset instance.""" """Reset instance."""
self.instance = None self.instance = None
@property
def injections(self):
"""Return tuple of all injections."""
return self.factory.injections
class ExternalDependency(Provider): class ExternalDependency(Provider):
"""External dependency provider. """External dependency provider.

View File

@ -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

View File

@ -11,15 +11,17 @@ follows `Semantic versioning`_
Development version Development version
------------------- -------------------
- Add functionality for decorating classes with ``@di.inject``.
- Add functionality for creating ``di.AbstractCatalog`` provider bundles. - Add functionality for creating ``di.AbstractCatalog`` provider bundles.
- Add enhancement for ``di.AbstractCatalog`` inheritance. - Enhance ``di.AbstractCatalog`` inheritance.
- Add images for catalog "Writing catalogs" and "Operating with catalogs" - Add images for catalog "Writing catalogs" and "Operating with catalogs"
examples. examples.
- Add support of Python 3.5. - Add functionality for using positional argument injections with
- Add support of six 1.10.0. ``di.Factory`` and ``di.Singleton`` providers.
- Add optimization for ``di.Injection.value`` property that will compute - Add optimization for ``di.Injection.value`` property that will compute
type of injection once, instead of doing this on every call. type of injection once, instead of doing this on every call.
- Add functionality for decorating classes with ``@di.inject``.
- Add support of Python 3.5.
- Add support of six 1.10.0.
- Add minor refactorings and code style fixes. - Add minor refactorings and code style fixes.
0.9.5 0.9.5

View File

@ -15,46 +15,57 @@ Nothing could be better than brief example:
Factory providers and __init__ injections Factory providers and __init__ injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``di.Factory`` takes a various number of keyword arguments that are ``di.Factory`` takes a various number of positional and keyword arguments that
transformed into keyword argument injections. Every time, when ``di.Factory`` are used as ``__init__()`` injections. Every time, when ``di.Factory``
creates new one instance, keyword argument injections would be passed as an creates new one instance, positional and keyword argument injections would be
instance's keyword arguments. passed as an instance's arguments.
All injectable values are provided *"as is"*, except of providers (subclasses Such behaviour is very similar to the standard Python ``functools.partial``
of ``di.Provider``). Providers will be called every time, when injection needs object, except of one thing: all injectable values are provided
to be done. For example, if injectable value of keyword argument injection is a *"as is"*, except of providers (subclasses of ``di.Provider``). Providers
``di.Factory``, it will provide new one instance (as a result of its call) as will be called every time, when injection needs to be done. For example,
an injectable value every time, when injection needs to be done. 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.
Example below is a little bit more complicated. It shows how to create Example below is a little bit more complicated. It shows how to create
``di.Factory`` of particular class with ``__init__`` keyword argument ``di.Factory`` of particular class with ``__init__()`` argument injections
injections which injectable values are also provided by another factories: which injectable values are also provided by another factories:
.. note:: .. note::
Current keyword argument injections syntax (in an example below) is a Current positional and keyword argument injections syntax (in the examples
**simplified one**. Full syntax and other types of injections could be below) is a **simplified one** version of full syntax. Examples of full
found in sections below. syntax and other types of injections could be found in sections below.
While keyword argument injections may be the best way of passing While positional / keyword argument injections may be the best way of
injections, current simplified syntax might be the preferable one and passing injections, current simplified syntax might be the preferable one
could be widely used. and could be widely used.
.. image:: /images/providers/factory_init_injections.png .. image:: /images/providers/factory_init_injections.png
:width: 90% :width: 90%
:align: center :align: center
.. literalinclude:: ../../examples/providers/factory_init_injections.py Example of usage positional argument injections:
.. literalinclude:: ../../examples/providers/factory_init_args.py
:language: python
Example of usage keyword argument injections:
.. literalinclude:: ../../examples/providers/factory_init_kwargs.py
:language: python :language: python
Factory providers and __init__ injections priority Factory providers and __init__ injections priority
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Next example shows how ``di.Factory`` provider deals with positional and Next example shows how ``di.Factory`` provider deals with positional and
keyword ``__init__`` context arguments. In few words, ``di.Factory`` keyword ``__init__()`` context arguments. In few words, ``di.Factory``
provider fully passes positional context arguments to class's ``__init__`` behaviour here is very like a standard Python ``functools.partial``:
method, but keyword context arguments have priority on predefined keyword
argument injections. - Positional context arguments will be appended after ``di.Factory``
positional injections.
- Keyword context arguments have priority on ``di.Factory`` keyword injections
and will be merged over them.
So, please, follow the example below: So, please, follow the example below:
@ -67,7 +78,7 @@ So, please, follow the example below:
Factory providers and other types of injections Factory providers and other types of injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Objects can take dependencies in different forms(some objects take init Objects can take dependencies in different forms (some objects take init
arguments, other use attributes setting or method calls). It affects how arguments, other use attributes setting or method calls). It affects how
such objects are created and initialized. such objects are created and initialized.
@ -78,8 +89,11 @@ All of those instructions are defined in ``di.injections`` module and are
subclasses of ``di.injections.Injection`` (shortcut ``di.Injection``). There subclasses of ``di.injections.Injection`` (shortcut ``di.Injection``). There
are several types of injections that are used by ``di.Factory`` provider: are several types of injections that are used by ``di.Factory`` provider:
+ ``di.Arg`` - injection is done by passing injectable value in object's
``__init__()`` method in time of object's creation as positional argument.
Takes injectable value only.
+ ``di.KwArg`` - injection is done by passing injectable value in object's + ``di.KwArg`` - injection is done by passing injectable value in object's
``__init__()`` method in time of object's creation via keyword argument. ``__init__()`` method in time of object's creation as keyword argument.
Takes keyword name of ``__init__()`` argument and injectable value. Takes keyword name of ``__init__()`` argument and injectable value.
+ ``di.Attribute`` - injection is done by setting specified attribute with + ``di.Attribute`` - injection is done by setting specified attribute with
injectable value right after object's creation. Takes attribute's name injectable value right after object's creation. Takes attribute's name

View File

@ -0,0 +1,34 @@
"""`di.Factory` providers with init positional injections example."""
import dependency_injector as di
class User(object):
"""Example class User."""
def __init__(self, main_photo):
"""Initializer."""
self.main_photo = main_photo
super(User, self).__init__()
class Photo(object):
"""Example class Photo."""
# User and Photo factories:
photos_factory = di.Factory(Photo)
users_factory = di.Factory(User, photos_factory)
# Creating several User objects:
user1 = users_factory() # Same as: user1 = User(Photo())
user2 = users_factory() # Same as: user2 = User(Photo())
# Making some asserts:
assert isinstance(user1, User)
assert isinstance(user1.main_photo, Photo)
assert isinstance(user2, User)
assert isinstance(user2.main_photo, Photo)
assert user1 is not user2
assert user1.main_photo is not user2.main_photo

View File

@ -62,7 +62,8 @@ assert user1.credit_card is not user2.credit_card
main_photo_mock = Photo() main_photo_mock = Photo()
credit_card_mock = CreditCard() credit_card_mock = CreditCard()
user3 = users_factory(3, main_photo=main_photo_mock, user3 = users_factory(3,
main_photo=main_photo_mock,
credit_card=credit_card_mock) credit_card=credit_card_mock)
assert user3.id == 3 assert user3.id == 3

View File

@ -1,4 +1,4 @@
"""`di.Factory` providers with init injections example.""" """`di.Factory` providers with init keyword injections example."""
import dependency_injector as di import dependency_injector as di
@ -17,8 +17,7 @@ class Photo(object):
# User and Photo factories: # User and Photo factories:
photos_factory = di.Factory(Photo) photos_factory = di.Factory(Photo)
users_factory = di.Factory(User, users_factory = di.Factory(User, main_photo=photos_factory)
main_photo=photos_factory)
# Creating several User objects: # Creating several User objects:
user1 = users_factory() # Same as: user1 = User(main_photo=Photo()) user1 = users_factory() # Same as: user1 = User(main_photo=Photo())

View File

@ -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):
@ -29,12 +28,20 @@ class InjectionTests(unittest.TestCase):
"""Test catalog.""" """Test catalog."""
provider = di.Provider() provider = di.Provider()
injection = di.Injection('some_arg_name', injection = di.Injection(TestCatalog.Bundle(TestCatalog.provider))
TestCatalog.Bundle(TestCatalog.provider))
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."""

View File

@ -4,6 +4,34 @@ import unittest2 as unittest
import dependency_injector as di import dependency_injector as di
class Example(object):
"""Example class for Factory provider tests."""
def __init__(self, init_arg1=None, init_arg2=None):
"""Initializer.
:param init_arg1:
:param init_arg2:
:return:
"""
self.init_arg1 = init_arg1
self.init_arg2 = init_arg2
self.attribute1 = None
self.attribute2 = None
self.method1_value = None
self.method2_value = None
def method1(self, value):
"""Setter method 1."""
self.method1_value = value
def method2(self, value):
"""Setter method 2."""
self.method2_value = value
class ProviderTests(unittest.TestCase): class ProviderTests(unittest.TestCase):
"""Provider test cases.""" """Provider test cases."""
@ -132,36 +160,9 @@ class DelegateTests(unittest.TestCase):
class FactoryTests(unittest.TestCase): class FactoryTests(unittest.TestCase):
"""Factory test cases.""" """Factory test cases."""
class Example(object):
"""Example class for Factory provider tests."""
def __init__(self, init_arg1=None, init_arg2=None):
"""Initializer.
:param init_arg1:
:param init_arg2:
:return:
"""
self.init_arg1 = init_arg1
self.init_arg2 = init_arg2
self.attribute1 = None
self.attribute2 = None
self.method1_value = None
self.method2_value = None
def method1(self, value):
"""Setter method 1."""
self.method1_value = value
def method2(self, value):
"""Setter method 2."""
self.method2_value = value
def test_is_provider(self): def test_is_provider(self):
"""Test `is_provider` check.""" """Test `is_provider` check."""
self.assertTrue(di.is_provider(di.Factory(self.Example))) self.assertTrue(di.is_provider(di.Factory(Example)))
def test_init_with_callable(self): def test_init_with_callable(self):
"""Test creation of provider with a callable.""" """Test creation of provider with a callable."""
@ -173,22 +174,20 @@ class FactoryTests(unittest.TestCase):
def test_call(self): def test_call(self):
"""Test creation of new instances.""" """Test creation of new instances."""
provider = di.Factory(self.Example) provider = di.Factory(Example)
instance1 = provider() instance1 = provider()
instance2 = provider() instance2 = provider()
self.assertIsNot(instance1, instance2) self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, self.Example) self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, self.Example) self.assertIsInstance(instance2, 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(Example, 'i1', 'i2')
init_arg1='i1',
init_arg2='i2')
instance1 = provider() instance1 = provider()
instance2 = provider() instance2 = provider()
@ -200,13 +199,56 @@ class FactoryTests(unittest.TestCase):
self.assertEqual(instance2.init_arg2, 'i2') self.assertEqual(instance2.init_arg2, 'i2')
self.assertIsNot(instance1, instance2) self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, self.Example) self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, self.Example) self.assertIsInstance(instance2, 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.
provider = di.Factory(self.Example,
di.KwArg('init_arg1', 'i1'), New simplified syntax.
"""
provider = di.Factory(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, Example)
self.assertIsInstance(instance2, 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(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, Example)
self.assertIsInstance(instance2, 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(Example,
di.Arg('i1'),
di.KwArg('init_arg2', 'i2')) di.KwArg('init_arg2', 'i2'))
instance1 = provider() instance1 = provider()
@ -219,12 +261,12 @@ class FactoryTests(unittest.TestCase):
self.assertEqual(instance2.init_arg2, 'i2') self.assertEqual(instance2.init_arg2, 'i2')
self.assertIsNot(instance1, instance2) self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, self.Example) self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, self.Example) self.assertIsInstance(instance2, Example)
def test_call_with_attributes(self): def test_call_with_attributes(self):
"""Test creation of new instances with attribute injections.""" """Test creation of new instances with attribute injections."""
provider = di.Factory(self.Example, provider = di.Factory(Example,
di.Attribute('attribute1', 'a1'), di.Attribute('attribute1', 'a1'),
di.Attribute('attribute2', 'a2')) di.Attribute('attribute2', 'a2'))
@ -238,12 +280,12 @@ class FactoryTests(unittest.TestCase):
self.assertEqual(instance2.attribute2, 'a2') self.assertEqual(instance2.attribute2, 'a2')
self.assertIsNot(instance1, instance2) self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, self.Example) self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, self.Example) self.assertIsInstance(instance2, Example)
def test_call_with_methods(self): def test_call_with_methods(self):
"""Test creation of new instances with method injections.""" """Test creation of new instances with method injections."""
provider = di.Factory(self.Example, provider = di.Factory(Example,
di.Method('method1', 'm1'), di.Method('method1', 'm1'),
di.Method('method2', 'm2')) di.Method('method2', 'm2'))
@ -257,12 +299,12 @@ class FactoryTests(unittest.TestCase):
self.assertEqual(instance2.method2_value, 'm2') self.assertEqual(instance2.method2_value, 'm2')
self.assertIsNot(instance1, instance2) self.assertIsNot(instance1, instance2)
self.assertIsInstance(instance1, self.Example) self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, self.Example) self.assertIsInstance(instance2, Example)
def test_call_with_context_args(self): def test_call_with_context_args(self):
"""Test creation of new instances with context args.""" """Test creation of new instances with context args."""
provider = di.Factory(self.Example) provider = di.Factory(Example)
instance = provider(11, 22) instance = provider(11, 22)
self.assertEqual(instance.init_arg1, 11) self.assertEqual(instance.init_arg1, 11)
@ -270,7 +312,7 @@ class FactoryTests(unittest.TestCase):
def test_call_with_context_kwargs(self): def test_call_with_context_kwargs(self):
"""Test creation of new instances with context kwargs.""" """Test creation of new instances with context kwargs."""
provider = di.Factory(self.Example, provider = di.Factory(Example,
di.KwArg('init_arg1', 1)) di.KwArg('init_arg1', 1))
instance1 = provider(init_arg2=22) instance1 = provider(init_arg2=22)
@ -283,7 +325,7 @@ class FactoryTests(unittest.TestCase):
def test_call_overridden(self): def test_call_overridden(self):
"""Test creation of new instances on overridden provider.""" """Test creation of new instances on overridden provider."""
provider = di.Factory(self.Example) provider = di.Factory(Example)
overriding_provider1 = di.Factory(dict) overriding_provider1 = di.Factory(dict)
overriding_provider2 = di.Factory(list) overriding_provider2 = di.Factory(list)
@ -298,9 +340,9 @@ class FactoryTests(unittest.TestCase):
self.assertIsInstance(instance2, list) self.assertIsInstance(instance2, list)
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 injections property."""
provider = di.Factory(self.Example, provider = di.Factory(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),
@ -315,16 +357,199 @@ class FactoryTests(unittest.TestCase):
class SingletonTests(unittest.TestCase): class SingletonTests(unittest.TestCase):
"""Singleton test cases.""" """Singleton test cases."""
def test_is_provider(self):
"""Test `is_provider` check."""
self.assertTrue(di.is_provider(di.Singleton(Example)))
def test_init_with_callable(self):
"""Test creation of provider with a callable."""
self.assertTrue(di.Singleton(credits))
def test_init_with_not_callable(self):
"""Test creation of provider with not a callable."""
self.assertRaises(di.Error, di.Singleton, 123)
def test_call(self): def test_call(self):
"""Test creation and returning of single object.""" """Test getting of instances."""
provider = di.Singleton(object) provider = di.Singleton(Example)
instance1 = provider()
instance2 = provider()
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_args(self):
"""Test getting of instances with init positional args.
New simplified syntax.
"""
provider = di.Singleton(Example, 'i1', 'i2')
instance1 = provider() instance1 = provider()
instance2 = 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.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_keyword_args(self):
"""Test getting of instances with init keyword args.
New simplified syntax.
"""
provider = di.Singleton(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.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_and_keyword_args(self):
"""Test getting of instances with init positional and keyword args.
Simplified syntax of positional and keyword arg injections.
"""
provider = di.Singleton(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.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_init_positional_and_keyword_args_extended_syntax(self):
"""Test getting of instances with init positional and keyword args.
Extended syntax of positional and keyword arg injections.
"""
provider = di.Singleton(Example,
di.Arg('i1'),
di.KwArg('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.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_attributes(self):
"""Test getting of instances with attribute injections."""
provider = di.Singleton(Example,
di.Attribute('attribute1', 'a1'),
di.Attribute('attribute2', 'a2'))
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.attribute1, 'a1')
self.assertEqual(instance1.attribute2, 'a2')
self.assertEqual(instance2.attribute1, 'a1')
self.assertEqual(instance2.attribute2, 'a2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_methods(self):
"""Test getting of instances with method injections."""
provider = di.Singleton(Example,
di.Method('method1', 'm1'),
di.Method('method2', 'm2'))
instance1 = provider()
instance2 = provider()
self.assertEqual(instance1.method1_value, 'm1')
self.assertEqual(instance1.method2_value, 'm2')
self.assertEqual(instance2.method1_value, 'm1')
self.assertEqual(instance2.method2_value, 'm2')
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, Example)
self.assertIsInstance(instance2, Example)
def test_call_with_context_args(self):
"""Test getting of instances with context args."""
provider = di.Singleton(Example)
instance = provider(11, 22)
self.assertEqual(instance.init_arg1, 11)
self.assertEqual(instance.init_arg2, 22)
def test_call_with_context_kwargs(self):
"""Test getting of instances with context kwargs."""
provider = di.Singleton(Example,
di.KwArg('init_arg1', 1))
instance1 = provider(init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
self.assertEqual(instance1.init_arg2, 22)
# Instance is created earlier
instance1 = provider(init_arg1=11, init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
self.assertEqual(instance1.init_arg2, 22)
def test_call_overridden(self):
"""Test getting of instances on overridden provider."""
provider = di.Singleton(Example)
overriding_provider1 = di.Singleton(dict)
overriding_provider2 = di.Singleton(object)
provider.override(overriding_provider1)
provider.override(overriding_provider2)
instance1 = provider()
instance2 = provider()
self.assertIs(instance1, instance2)
self.assertIsInstance(instance1, object) self.assertIsInstance(instance1, object)
self.assertIsInstance(instance2, object) self.assertIsInstance(instance2, object)
self.assertIs(instance1, instance2)
def test_injections(self):
"""Test getting a full list of injections using injections property."""
provider = di.Singleton(Example,
di.Arg(1),
di.KwArg('init_arg2', 2),
di.Attribute('attribute1', 3),
di.Attribute('attribute2', 4),
di.Method('method1', 5),
di.Method('method2', 6))
injections = provider.injections
self.assertEquals(len(injections), 6)
def test_reset(self): def test_reset(self):
"""Test creation and reset of single object.""" """Test creation and reset of single object."""

View File

@ -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."""