mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 09:36:48 +03:00
Merge remote-tracking branch 'origin/enhancement_of_catalog_inheritance'
This commit is contained in:
commit
b7bab4938f
|
@ -29,6 +29,7 @@ from .utils import ensure_is_injection
|
|||
from .utils import is_kwarg_injection
|
||||
from .utils import is_attribute_injection
|
||||
from .utils import is_method_injection
|
||||
from .utils import is_catalog
|
||||
|
||||
from .errors import Error
|
||||
|
||||
|
@ -67,6 +68,7 @@ __all__ = (
|
|||
'is_kwarg_injection',
|
||||
'is_attribute_injection',
|
||||
'is_method_injection',
|
||||
'is_catalog',
|
||||
|
||||
# Errors
|
||||
'Error',
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
import six
|
||||
|
||||
from .errors import Error
|
||||
|
||||
from .utils import is_provider
|
||||
from .utils import is_catalog
|
||||
|
||||
|
||||
class CatalogMetaClass(type):
|
||||
|
@ -12,26 +14,47 @@ class CatalogMetaClass(type):
|
|||
|
||||
def __new__(mcs, class_name, bases, attributes):
|
||||
"""Meta class factory."""
|
||||
providers = dict()
|
||||
new_attributes = dict()
|
||||
for name, value in six.iteritems(attributes):
|
||||
if is_provider(value):
|
||||
providers[name] = value
|
||||
new_attributes[name] = value
|
||||
cls_providers = dict((name, provider)
|
||||
for name, provider in six.iteritems(attributes)
|
||||
if is_provider(provider))
|
||||
|
||||
cls = type.__new__(mcs, class_name, bases, new_attributes)
|
||||
cls.providers = cls.providers.copy()
|
||||
cls.providers.update(providers)
|
||||
return cls
|
||||
inherited_providers = dict((name, provider)
|
||||
for base in bases if is_catalog(base)
|
||||
for name, provider in six.iteritems(
|
||||
base.providers))
|
||||
|
||||
providers = dict()
|
||||
providers.update(cls_providers)
|
||||
providers.update(inherited_providers)
|
||||
|
||||
attributes['cls_providers'] = cls_providers
|
||||
attributes['inherited_providers'] = inherited_providers
|
||||
attributes['providers'] = providers
|
||||
return type.__new__(mcs, class_name, bases, attributes)
|
||||
|
||||
|
||||
@six.add_metaclass(CatalogMetaClass)
|
||||
class AbstractCatalog(object):
|
||||
|
||||
"""Abstract providers catalog."""
|
||||
"""Abstract providers catalog.
|
||||
|
||||
:type providers: dict[str, dependency_injector.Provider]
|
||||
:param providers: Dict of all catalog providers, including inherited from
|
||||
parent catalogs
|
||||
|
||||
:type cls_providers: dict[str, dependency_injector.Provider]
|
||||
:param cls_providers: Dict of current catalog providers
|
||||
|
||||
:type inherited_providers: dict[str, dependency_injector.Provider]
|
||||
:param inherited_providers: Dict of providers, that are inherited from
|
||||
parent catalogs
|
||||
"""
|
||||
|
||||
providers = dict()
|
||||
cls_providers = dict()
|
||||
inherited_providers = dict()
|
||||
|
||||
__IS_CATALOG__ = True
|
||||
__slots__ = ('used_providers',)
|
||||
|
||||
def __init__(self, *used_providers):
|
||||
|
@ -41,7 +64,7 @@ class AbstractCatalog(object):
|
|||
def __getattribute__(self, item):
|
||||
"""Return providers."""
|
||||
attribute = super(AbstractCatalog, self).__getattribute__(item)
|
||||
if item in ('providers', 'used_providers',):
|
||||
if item in ('providers', 'used_providers', '__class__'):
|
||||
return attribute
|
||||
|
||||
if attribute not in self.used_providers:
|
||||
|
@ -62,7 +85,7 @@ class AbstractCatalog(object):
|
|||
|
||||
:type overriding: AbstractCatalog
|
||||
"""
|
||||
for name, provider in six.iteritems(overriding.providers):
|
||||
for name, provider in six.iteritems(overriding.cls_providers):
|
||||
cls.providers[name].override(provider)
|
||||
|
||||
|
||||
|
|
|
@ -56,6 +56,12 @@ def is_method_injection(instance):
|
|||
getattr(instance, '__IS_METHOD_INJECTION__', False) is True)
|
||||
|
||||
|
||||
def is_catalog(instance):
|
||||
"""Check if instance is catalog instance."""
|
||||
return (isinstance(instance, six.class_types) and
|
||||
getattr(instance, '__IS_CATALOG__', False) is True)
|
||||
|
||||
|
||||
def get_injectable_kwargs(kwargs, injections):
|
||||
"""Return dictionary of kwargs, patched with injections."""
|
||||
init_kwargs = dict(((injection.name, injection.value)
|
||||
|
|
|
@ -45,16 +45,23 @@ Example:
|
|||
Operating with catalog providers
|
||||
--------------------------------
|
||||
|
||||
There are several things that could be useful for operating with catalog
|
||||
providers:
|
||||
``di.AbstractCatalog`` has several features that could be useful for some kind
|
||||
of operations on catalog's providers:
|
||||
|
||||
- First of all, ``di.AbstractCatalog.providers`` attribute contains ``dict``
|
||||
with all catalog providers. This dictionary could be used for any kind of
|
||||
operations that could be done with providers. The only note, is that
|
||||
``di.AbstractCatalog.providers`` attribute is read-only.
|
||||
- Second one, ``di.AbstractCatalog.filter(provider_type=di.Provider)`` method
|
||||
could be used for filtering catalog providers by provider types (for example,
|
||||
for getting all ``di.Factory`` providers).
|
||||
- ``di.AbstractCatalog.providers`` is read-only attribute that contains
|
||||
``dict`` of all catalog providers, including providers that are inherited
|
||||
from parent catalogs, where key is the name of provider and value is
|
||||
provider itself.
|
||||
- ``di.AbstractCatalog.cls_providers`` is read-only attribute contains ``dict``
|
||||
of current catalog providers, where key is the name of provider and value is
|
||||
provider itself.
|
||||
- ``di.AbstractCatalog.inherited_providers`` is read-only attribute contains
|
||||
``dict`` of all providers that are inherited from parent catalogs, where key
|
||||
is the name of provider and value is provider itself.
|
||||
- ``di.AbstractCatalog.filter(provider_type=di.Provider)`` is a class method
|
||||
that could be used for filtering catalog providers by provider types
|
||||
(for example, for getting all ``di.Factory`` providers).
|
||||
``di.AbstractCatalog.filter()`` method use ``di.AbstractCatalog.providers``.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ Development version
|
|||
-------------------
|
||||
|
||||
- Add functionality for decorating classes with ``@di.inject``.
|
||||
- Add enhancement for ``di.AbstractCatalog`` inheritance.
|
||||
|
||||
0.9.5
|
||||
-----
|
||||
|
|
|
@ -3,29 +3,35 @@
|
|||
import dependency_injector as di
|
||||
|
||||
|
||||
class Catalog(di.AbstractCatalog):
|
||||
class CatalogA(di.AbstractCatalog):
|
||||
|
||||
"""Providers catalog."""
|
||||
"""Example catalog A."""
|
||||
|
||||
provider1 = di.Factory(object)
|
||||
""":type: (di.Provider) -> object"""
|
||||
|
||||
provider2 = di.Factory(object)
|
||||
""":type: (di.Provider) -> object"""
|
||||
|
||||
provider3 = di.Singleton(object)
|
||||
""":type: (di.Provider) -> object"""
|
||||
class CatalogB(CatalogA):
|
||||
|
||||
provider4 = di.Singleton(object)
|
||||
"""Example catalog B."""
|
||||
|
||||
provider2 = di.Singleton(object)
|
||||
""":type: (di.Provider) -> object"""
|
||||
|
||||
|
||||
# Making some asserts:
|
||||
assert Catalog.providers == dict(provider1=Catalog.provider1,
|
||||
provider2=Catalog.provider2,
|
||||
provider3=Catalog.provider3,
|
||||
provider4=Catalog.provider4)
|
||||
assert Catalog.filter(di.Factory) == dict(provider1=Catalog.provider1,
|
||||
provider2=Catalog.provider2)
|
||||
assert Catalog.filter(di.Singleton) == dict(provider3=Catalog.provider3,
|
||||
provider4=Catalog.provider4)
|
||||
# Making some asserts for `providers` attribute:
|
||||
assert CatalogA.providers == dict(provider1=CatalogA.provider1)
|
||||
assert CatalogB.providers == dict(provider1=CatalogA.provider1,
|
||||
provider2=CatalogB.provider2)
|
||||
|
||||
# Making some asserts for `cls_providers` attribute:
|
||||
assert CatalogA.cls_providers == dict(provider1=CatalogA.provider1)
|
||||
assert CatalogB.cls_providers == dict(provider2=CatalogB.provider2)
|
||||
|
||||
# Making some asserts for `inherited_providers` attribute:
|
||||
assert CatalogA.inherited_providers == dict()
|
||||
assert CatalogB.inherited_providers == dict(provider1=CatalogA.provider1)
|
||||
|
||||
# Making some asserts for `filter()` method:
|
||||
assert CatalogB.filter(di.Factory) == dict(provider1=CatalogA.provider1)
|
||||
assert CatalogB.filter(di.Singleton) == dict(provider2=CatalogB.provider2)
|
||||
|
|
|
@ -4,6 +4,74 @@ import unittest2 as unittest
|
|||
import dependency_injector as di
|
||||
|
||||
|
||||
class CatalogsInheritanceTests(unittest.TestCase):
|
||||
|
||||
"""Catalogs inheritance tests."""
|
||||
|
||||
class CatalogA(di.AbstractCatalog):
|
||||
|
||||
"""Test catalog A."""
|
||||
|
||||
p11 = di.Provider()
|
||||
p12 = di.Provider()
|
||||
|
||||
class CatalogB(CatalogA):
|
||||
|
||||
"""Test catalog B."""
|
||||
|
||||
p21 = di.Provider()
|
||||
p22 = di.Provider()
|
||||
|
||||
class CatalogC(CatalogB):
|
||||
|
||||
"""Test catalog C."""
|
||||
|
||||
p31 = di.Provider()
|
||||
p32 = di.Provider()
|
||||
|
||||
def test_cls_providers(self):
|
||||
"""Test `di.AbstractCatalog.cls_providers` contents."""
|
||||
self.assertDictEqual(self.CatalogA.cls_providers,
|
||||
dict(p11=self.CatalogA.p11,
|
||||
p12=self.CatalogA.p12))
|
||||
self.assertDictEqual(self.CatalogB.cls_providers,
|
||||
dict(p21=self.CatalogB.p21,
|
||||
p22=self.CatalogB.p22))
|
||||
self.assertDictEqual(self.CatalogC.cls_providers,
|
||||
dict(p31=self.CatalogC.p31,
|
||||
p32=self.CatalogC.p32))
|
||||
|
||||
def test_inherited_providers(self):
|
||||
"""Test `di.AbstractCatalog.inherited_providers` contents."""
|
||||
self.assertDictEqual(self.CatalogA.inherited_providers, dict())
|
||||
self.assertDictEqual(self.CatalogB.inherited_providers,
|
||||
dict(p11=self.CatalogA.p11,
|
||||
p12=self.CatalogA.p12))
|
||||
self.assertDictEqual(self.CatalogC.inherited_providers,
|
||||
dict(p11=self.CatalogA.p11,
|
||||
p12=self.CatalogA.p12,
|
||||
p21=self.CatalogB.p21,
|
||||
p22=self.CatalogB.p22))
|
||||
|
||||
def test_providers(self):
|
||||
"""Test `di.AbstractCatalog.inherited_providers` contents."""
|
||||
self.assertDictEqual(self.CatalogA.providers,
|
||||
dict(p11=self.CatalogA.p11,
|
||||
p12=self.CatalogA.p12))
|
||||
self.assertDictEqual(self.CatalogB.providers,
|
||||
dict(p11=self.CatalogA.p11,
|
||||
p12=self.CatalogA.p12,
|
||||
p21=self.CatalogB.p21,
|
||||
p22=self.CatalogB.p22))
|
||||
self.assertDictEqual(self.CatalogC.providers,
|
||||
dict(p11=self.CatalogA.p11,
|
||||
p12=self.CatalogA.p12,
|
||||
p21=self.CatalogB.p21,
|
||||
p22=self.CatalogB.p22,
|
||||
p31=self.CatalogC.p31,
|
||||
p32=self.CatalogC.p32))
|
||||
|
||||
|
||||
class CatalogTests(unittest.TestCase):
|
||||
|
||||
"""Catalog test cases."""
|
||||
|
@ -25,44 +93,11 @@ class CatalogTests(unittest.TestCase):
|
|||
catalog = self.Catalog()
|
||||
self.assertRaises(di.Error, getattr, catalog, 'obj')
|
||||
|
||||
def test_all_providers(self):
|
||||
"""Test getting of all catalog providers."""
|
||||
self.assertTrue(len(self.Catalog.providers) == 2)
|
||||
|
||||
self.assertIn('obj', self.Catalog.providers)
|
||||
self.assertIn(self.Catalog.obj, self.Catalog.providers.values())
|
||||
|
||||
self.assertIn('another_obj', self.Catalog.providers)
|
||||
self.assertIn(self.Catalog.another_obj,
|
||||
self.Catalog.providers.values())
|
||||
|
||||
def test_all_providers_by_type(self):
|
||||
"""Test getting of all catalog providers of specific type."""
|
||||
self.assertTrue(len(self.Catalog.filter(di.Object)) == 2)
|
||||
self.assertTrue(len(self.Catalog.filter(di.Value)) == 0)
|
||||
|
||||
def test_metaclass_with_several_catalogs(self):
|
||||
"""Test that metaclass work well with several catalogs."""
|
||||
class Catalog1(di.AbstractCatalog):
|
||||
|
||||
"""Catalog1."""
|
||||
|
||||
provider = di.Object(object())
|
||||
|
||||
class Catalog2(di.AbstractCatalog):
|
||||
|
||||
"""Catalog2."""
|
||||
|
||||
provider = di.Object(object())
|
||||
|
||||
self.assertTrue(len(Catalog1.providers) == 1)
|
||||
self.assertIs(Catalog1.provider, Catalog1.providers['provider'])
|
||||
|
||||
self.assertTrue(len(Catalog2.providers) == 1)
|
||||
self.assertIs(Catalog2.provider, Catalog2.providers['provider'])
|
||||
|
||||
self.assertIsNot(Catalog1.provider, Catalog2.provider)
|
||||
|
||||
|
||||
class OverrideTests(unittest.TestCase):
|
||||
|
||||
|
|
|
@ -190,3 +190,32 @@ class IsMethodInjectionTests(unittest.TestCase):
|
|||
def test_with_object(self):
|
||||
"""Test with object."""
|
||||
self.assertFalse(di.is_method_injection(object()))
|
||||
|
||||
|
||||
class IsCatalogTests(unittest.TestCase):
|
||||
|
||||
"""`is_catalog()` test cases."""
|
||||
|
||||
def test_with_cls(self):
|
||||
"""Test with class."""
|
||||
self.assertTrue(di.is_catalog(di.AbstractCatalog))
|
||||
|
||||
def test_with_instance(self):
|
||||
"""Test with class."""
|
||||
self.assertFalse(di.is_catalog(di.AbstractCatalog()))
|
||||
|
||||
def test_with_child_class(self):
|
||||
"""Test with parent class."""
|
||||
class Catalog(di.AbstractCatalog):
|
||||
|
||||
"""Example catalog child class."""
|
||||
|
||||
self.assertTrue(di.is_catalog(Catalog))
|
||||
|
||||
def test_with_string(self):
|
||||
"""Test with string."""
|
||||
self.assertFalse(di.is_catalog('some_string'))
|
||||
|
||||
def test_with_object(self):
|
||||
"""Test with object."""
|
||||
self.assertFalse(di.is_catalog(object()))
|
||||
|
|
Loading…
Reference in New Issue
Block a user