mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-22 17:47:02 +03:00
Add catalog packs
This commit is contained in:
parent
3b54763563
commit
730d46f9a0
|
@ -1,7 +1,6 @@
|
||||||
"""Dependency injector."""
|
"""Dependency injector."""
|
||||||
|
|
||||||
from .catalog import AbstractCatalog
|
from .catalog import AbstractCatalog
|
||||||
from .catalog import Subset
|
|
||||||
from .catalog import override
|
from .catalog import override
|
||||||
|
|
||||||
from .providers import Provider
|
from .providers import Provider
|
||||||
|
@ -31,7 +30,6 @@ 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 is_catalog
|
from .utils import is_catalog
|
||||||
from .utils import is_catalog_subset
|
|
||||||
|
|
||||||
from .errors import Error
|
from .errors import Error
|
||||||
|
|
||||||
|
@ -39,7 +37,6 @@ from .errors import Error
|
||||||
__all__ = (
|
__all__ = (
|
||||||
# Catalogs
|
# Catalogs
|
||||||
'AbstractCatalog',
|
'AbstractCatalog',
|
||||||
'Subset',
|
|
||||||
'override',
|
'override',
|
||||||
|
|
||||||
# Providers
|
# Providers
|
||||||
|
@ -72,7 +69,6 @@ __all__ = (
|
||||||
'is_attribute_injection',
|
'is_attribute_injection',
|
||||||
'is_method_injection',
|
'is_method_injection',
|
||||||
'is_catalog',
|
'is_catalog',
|
||||||
'is_catalog_subset',
|
|
||||||
|
|
||||||
# Errors
|
# Errors
|
||||||
'Error',
|
'Error',
|
||||||
|
|
|
@ -8,6 +8,67 @@ from .utils import is_provider
|
||||||
from .utils import is_catalog
|
from .utils import is_catalog
|
||||||
|
|
||||||
|
|
||||||
|
class CatalogPackFactory(object):
|
||||||
|
"""Factory of catalog packs."""
|
||||||
|
|
||||||
|
def __init__(self, catalog):
|
||||||
|
"""Initializer."""
|
||||||
|
self.catalog = catalog
|
||||||
|
|
||||||
|
def __call__(self, *providers):
|
||||||
|
"""Create catalog pack with specified providers."""
|
||||||
|
return CatalogPack(self.catalog, *providers)
|
||||||
|
|
||||||
|
|
||||||
|
class CatalogPack(object):
|
||||||
|
"""Pack of catalog providers."""
|
||||||
|
|
||||||
|
__slots__ = ('catalog', 'providers', '__dict__')
|
||||||
|
|
||||||
|
def __init__(self, catalog, *providers):
|
||||||
|
"""Initializer."""
|
||||||
|
self.catalog = catalog
|
||||||
|
self.providers = dict()
|
||||||
|
for provider in providers:
|
||||||
|
self._ensure_provider_is_bound(provider)
|
||||||
|
self.__dict__[provider.bind.name] = provider
|
||||||
|
self.providers[provider.bind.name] = provider
|
||||||
|
super(CatalogPack, self).__init__()
|
||||||
|
|
||||||
|
def get(self, name):
|
||||||
|
"""Return provider with specified name or raises error."""
|
||||||
|
try:
|
||||||
|
return self.providers[name]
|
||||||
|
except KeyError:
|
||||||
|
self._raise_undefined_provider_error(name)
|
||||||
|
|
||||||
|
def has(self, name):
|
||||||
|
"""Check if there is provider with certain name."""
|
||||||
|
return name in self.providers
|
||||||
|
|
||||||
|
def _ensure_provider_is_bound(self, provider):
|
||||||
|
"""Check that provider is bound."""
|
||||||
|
if not provider.bind:
|
||||||
|
raise Error('Provider {0} is not bound to '
|
||||||
|
'any catalog'.format(provider))
|
||||||
|
if provider is not self.catalog.get(provider.bind.name):
|
||||||
|
raise Error('{0} can contain providers from '
|
||||||
|
'catalog {0}' .format(self, self.catalog))
|
||||||
|
|
||||||
|
def _raise_undefined_provider_error(self, name):
|
||||||
|
"""Raise error for cases when there is no such provider in pack."""
|
||||||
|
raise Error('Provider "{0}" is not a part of {1}'.format(name, self))
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
"""Raise an error on every attempt to get undefined provider."""
|
||||||
|
self._raise_undefined_provider_error(item)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Return string representation of pack."""
|
||||||
|
return '<Catalog pack ({0}), {1}>'.format(
|
||||||
|
', '.join(six.iterkeys(self.providers)), self.catalog)
|
||||||
|
|
||||||
|
|
||||||
class CatalogMetaClass(type):
|
class CatalogMetaClass(type):
|
||||||
"""Providers catalog meta class."""
|
"""Providers catalog meta class."""
|
||||||
|
|
||||||
|
@ -26,12 +87,14 @@ class CatalogMetaClass(type):
|
||||||
providers.update(cls_providers)
|
providers.update(cls_providers)
|
||||||
providers.update(inherited_providers)
|
providers.update(inherited_providers)
|
||||||
|
|
||||||
attributes['cls_providers'] = cls_providers
|
|
||||||
attributes['inherited_providers'] = inherited_providers
|
|
||||||
attributes['providers'] = providers
|
|
||||||
|
|
||||||
cls = type.__new__(mcs, class_name, bases, attributes)
|
cls = type.__new__(mcs, class_name, bases, attributes)
|
||||||
|
|
||||||
|
cls.cls_providers = cls_providers
|
||||||
|
cls.inherited_providers = inherited_providers
|
||||||
|
cls.providers = providers
|
||||||
|
|
||||||
|
cls.Pack = CatalogPackFactory(cls)
|
||||||
|
|
||||||
for name, provider in six.iteritems(cls_providers):
|
for name, provider in six.iteritems(cls_providers):
|
||||||
provider.bind = ProviderBinding(cls, name)
|
provider.bind = ProviderBinding(cls, name)
|
||||||
|
|
||||||
|
@ -56,28 +119,19 @@ class AbstractCatalog(object):
|
||||||
:type inherited_providers: dict[str, dependency_injector.Provider]
|
:type inherited_providers: dict[str, dependency_injector.Provider]
|
||||||
:param inherited_providers: Dict of providers, that are inherited from
|
:param inherited_providers: Dict of providers, that are inherited from
|
||||||
parent catalogs
|
parent catalogs
|
||||||
|
|
||||||
|
:type Pack: CatalogPack
|
||||||
|
:param Pack: Catalog's pack class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
providers = dict()
|
Pack = CatalogPackFactory
|
||||||
|
|
||||||
cls_providers = dict()
|
cls_providers = dict()
|
||||||
inherited_providers = dict()
|
inherited_providers = dict()
|
||||||
|
providers = dict()
|
||||||
|
|
||||||
__IS_CATALOG__ = True
|
__IS_CATALOG__ = True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def subset(cls, *provider_names):
|
|
||||||
"""Catalog subset factory.
|
|
||||||
|
|
||||||
Create subset of catalog providers using provider names.
|
|
||||||
"""
|
|
||||||
return Subset(*(cls.get(provider_name)
|
|
||||||
for provider_name in provider_names))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def is_subset_owner(cls, subset):
|
|
||||||
"""Check if catalog is subset owner."""
|
|
||||||
return subset.catalog is cls
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def filter(cls, provider_type):
|
def filter(cls, provider_type):
|
||||||
"""Return dict of providers, that are instance of provided type."""
|
"""Return dict of providers, that are instance of provided type."""
|
||||||
|
@ -120,64 +174,6 @@ class ProviderBinding(object):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
class Subset(object):
|
|
||||||
"""Subset of catalog providers."""
|
|
||||||
|
|
||||||
__IS_SUBSET__ = True
|
|
||||||
__slots__ = ('catalog', 'providers', '__dict__')
|
|
||||||
|
|
||||||
def __init__(self, *providers):
|
|
||||||
"""Initializer."""
|
|
||||||
if not providers:
|
|
||||||
raise Error('Subset could not be initialized without providers')
|
|
||||||
|
|
||||||
first_provider = providers[0]
|
|
||||||
self.catalog = self._get_provider_binding(first_provider).catalog
|
|
||||||
|
|
||||||
self.providers = dict()
|
|
||||||
for provider in providers:
|
|
||||||
provider_bind = self._get_provider_binding(provider)
|
|
||||||
if not self.catalog.get(provider_bind.name) is provider:
|
|
||||||
raise Error('Subset can contain providers from '
|
|
||||||
'one catalog {0}, '
|
|
||||||
'unknown provider - {1}'.format(self.catalog,
|
|
||||||
provider))
|
|
||||||
self.providers[provider_bind.name] = provider
|
|
||||||
self.__dict__[provider_bind.name] = provider
|
|
||||||
super(Subset, self).__init__()
|
|
||||||
|
|
||||||
def get(self, name):
|
|
||||||
"""Return provider with specified name or raises error."""
|
|
||||||
try:
|
|
||||||
return self.providers[name]
|
|
||||||
except KeyError:
|
|
||||||
self._raise_undefined_provider_error(name)
|
|
||||||
|
|
||||||
def has(self, name):
|
|
||||||
"""Check if there is provider with certain name."""
|
|
||||||
return name in self.providers
|
|
||||||
|
|
||||||
def __getattr__(self, item):
|
|
||||||
"""Raise an error on every attempt to get undefined provider."""
|
|
||||||
self._raise_undefined_provider_error(item)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"""Return string representation of subset."""
|
|
||||||
return '<Subset ({0}), {1}>'.format(
|
|
||||||
', '.join(six.iterkeys(self.providers)), self.catalog)
|
|
||||||
|
|
||||||
def _get_provider_binding(self, provider):
|
|
||||||
"""Return provider binding or raise error if provider is not boud."""
|
|
||||||
if not provider.bind:
|
|
||||||
raise Error('Provider {0} is not bound to '
|
|
||||||
'any catalog'.format(provider))
|
|
||||||
return provider.bind
|
|
||||||
|
|
||||||
def _raise_undefined_provider_error(self, name):
|
|
||||||
"""Raise error for cases when there is no such provider in subset."""
|
|
||||||
raise Error('Provider "{0}" is not a part of {1}'.format(name, self))
|
|
||||||
|
|
||||||
|
|
||||||
def override(catalog):
|
def override(catalog):
|
||||||
"""Catalog overriding decorator."""
|
"""Catalog overriding decorator."""
|
||||||
def decorator(overriding_catalog):
|
def decorator(overriding_catalog):
|
||||||
|
|
|
@ -62,12 +62,6 @@ def is_catalog(instance):
|
||||||
getattr(instance, '__IS_CATALOG__', False) is True)
|
getattr(instance, '__IS_CATALOG__', False) is True)
|
||||||
|
|
||||||
|
|
||||||
def is_catalog_subset(instance):
|
|
||||||
"""Check if instance is catalog subset instance."""
|
|
||||||
return (not isinstance(instance, six.class_types) and
|
|
||||||
getattr(instance, '__IS_SUBSET__', False) is True)
|
|
||||||
|
|
||||||
|
|
||||||
def get_injectable_kwargs(kwargs, injections):
|
def get_injectable_kwargs(kwargs, injections):
|
||||||
"""Return dictionary of kwargs, patched with injections."""
|
"""Return dictionary of kwargs, patched with injections."""
|
||||||
init_kwargs = dict(((injection.name, injection.value)
|
init_kwargs = dict(((injection.name, injection.value)
|
||||||
|
|
|
@ -35,13 +35,12 @@ class AuthView(BaseWebView):
|
||||||
class PhotosView(BaseWebView):
|
class PhotosView(BaseWebView):
|
||||||
"""Example photo processing web view."""
|
"""Example photo processing web view."""
|
||||||
|
|
||||||
# Creating example views with appropriate service provider subsets:
|
# Creating example views with appropriate service provider packs:
|
||||||
auth_view = AuthView(di.Subset(Services.users,
|
auth_view = AuthView(Services.Pack(Services.users,
|
||||||
Services.auth))
|
Services.auth))
|
||||||
photos_view = PhotosView(di.Subset(Services.users,
|
photos_view = PhotosView(Services.Pack(Services.users,
|
||||||
Services.photos))
|
Services.photos))
|
||||||
|
|
||||||
|
|
||||||
# Making some asserts:
|
# Making some asserts:
|
||||||
assert auth_view.services.users is Services.users
|
assert auth_view.services.users is Services.users
|
||||||
assert auth_view.services.auth is Services.auth
|
assert auth_view.services.auth is Services.auth
|
|
@ -25,6 +25,13 @@ class CatalogC(CatalogB):
|
||||||
p32 = di.Provider()
|
p32 = di.Provider()
|
||||||
|
|
||||||
|
|
||||||
|
class CatalogD(di.AbstractCatalog):
|
||||||
|
"""Test catalog D."""
|
||||||
|
|
||||||
|
p11 = di.Provider()
|
||||||
|
p12 = di.Provider()
|
||||||
|
|
||||||
|
|
||||||
class CatalogsInheritanceTests(unittest.TestCase):
|
class CatalogsInheritanceTests(unittest.TestCase):
|
||||||
"""Catalogs inheritance tests."""
|
"""Catalogs inheritance tests."""
|
||||||
|
|
||||||
|
@ -71,59 +78,55 @@ class CatalogsInheritanceTests(unittest.TestCase):
|
||||||
p32=CatalogC.p32))
|
p32=CatalogC.p32))
|
||||||
|
|
||||||
|
|
||||||
class _BasicSubsetTests(object):
|
class CatalogPackTests(unittest.TestCase):
|
||||||
"""Catalog subset test cases."""
|
"""Catalog pack test cases."""
|
||||||
|
|
||||||
def test_get_attr_from_subset(self):
|
def setUp(self):
|
||||||
"""Test get providers (attribute) from subset."""
|
"""Set test environment up."""
|
||||||
self.assertIs(self.subset.p11, CatalogC.p11)
|
self.pack = CatalogC.Pack(CatalogC.p11,
|
||||||
self.assertIs(self.subset.p12, CatalogC.p12)
|
CatalogC.p12)
|
||||||
|
|
||||||
def test_get_attr_not_from_subset(self):
|
def test_get_attr_from_pack(self):
|
||||||
"""Test get providers (attribute) that are not in subset."""
|
"""Test get providers (attribute) from pack."""
|
||||||
self.assertRaises(di.Error, getattr, self.subset, 'p21')
|
self.assertIs(self.pack.p11, CatalogC.p11)
|
||||||
self.assertRaises(di.Error, getattr, self.subset, 'p22')
|
self.assertIs(self.pack.p12, CatalogC.p12)
|
||||||
self.assertRaises(di.Error, getattr, self.subset, 'p31')
|
|
||||||
self.assertRaises(di.Error, getattr, self.subset, 'p32')
|
|
||||||
|
|
||||||
def test_get_method_from_subset(self):
|
def test_get_attr_not_from_pack(self):
|
||||||
"""Test get providers (get() method) from subset."""
|
"""Test get providers (attribute) that are not in pack."""
|
||||||
self.assertIs(self.subset.get('p11'), CatalogC.p11)
|
self.assertRaises(di.Error, getattr, self.pack, 'p21')
|
||||||
self.assertIs(self.subset.get('p12'), CatalogC.p12)
|
self.assertRaises(di.Error, getattr, self.pack, 'p22')
|
||||||
|
self.assertRaises(di.Error, getattr, self.pack, 'p31')
|
||||||
|
self.assertRaises(di.Error, getattr, self.pack, 'p32')
|
||||||
|
|
||||||
def test_get_method_not_from_subset(self):
|
def test_get_method_from_pack(self):
|
||||||
"""Test get providers (get() method) that are not in subset."""
|
"""Test get providers (get() method) from pack."""
|
||||||
self.assertRaises(di.Error, self.subset.get, 'p21')
|
self.assertIs(self.pack.get('p11'), CatalogC.p11)
|
||||||
self.assertRaises(di.Error, self.subset.get, 'p22')
|
self.assertIs(self.pack.get('p12'), CatalogC.p12)
|
||||||
self.assertRaises(di.Error, self.subset.get, 'p31')
|
|
||||||
self.assertRaises(di.Error, self.subset.get, 'p32')
|
def test_get_method_not_from_pack(self):
|
||||||
|
"""Test get providers (get() method) that are not in pack."""
|
||||||
|
self.assertRaises(di.Error, self.pack.get, 'p21')
|
||||||
|
self.assertRaises(di.Error, self.pack.get, 'p22')
|
||||||
|
self.assertRaises(di.Error, self.pack.get, 'p31')
|
||||||
|
self.assertRaises(di.Error, self.pack.get, 'p32')
|
||||||
|
|
||||||
def test_has(self):
|
def test_has(self):
|
||||||
"""Test checks of providers availability in subsets."""
|
"""Test checks of providers availability in pack."""
|
||||||
self.assertTrue(self.subset.has('p11'))
|
self.assertTrue(self.pack.has('p11'))
|
||||||
self.assertTrue(self.subset.has('p12'))
|
self.assertTrue(self.pack.has('p12'))
|
||||||
|
|
||||||
self.assertFalse(self.subset.has('p21'))
|
self.assertFalse(self.pack.has('p21'))
|
||||||
self.assertFalse(self.subset.has('p22'))
|
self.assertFalse(self.pack.has('p22'))
|
||||||
self.assertFalse(self.subset.has('p31'))
|
self.assertFalse(self.pack.has('p31'))
|
||||||
self.assertFalse(self.subset.has('p32'))
|
self.assertFalse(self.pack.has('p32'))
|
||||||
|
|
||||||
|
def test_create_pack_with_another_catalog_provider(self):
|
||||||
|
"""Test that pack is not created with provider from another catalog."""
|
||||||
|
self.assertRaises(di.Error, CatalogC.Pack, CatalogC.p31, CatalogD.p11)
|
||||||
|
|
||||||
class SubsetCatalogFactoryTests(_BasicSubsetTests, unittest.TestCase):
|
def test_create_pack_with_unbound_provider(self):
|
||||||
"""Subset, that is created by catalog factory method, tests."""
|
"""Test that pack is not created with unbound provider."""
|
||||||
|
self.assertRaises(di.Error, CatalogC.Pack, di.Provider())
|
||||||
def setUp(self):
|
|
||||||
"""Set test environment up."""
|
|
||||||
self.subset = CatalogC.subset('p11', 'p12')
|
|
||||||
|
|
||||||
|
|
||||||
class SubsetProvidersAggregationTests(_BasicSubsetTests, unittest.TestCase):
|
|
||||||
"""Subset, that is created catalog providers aggregation method, tests."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
"""Set test environment up."""
|
|
||||||
self.subset = di.Subset(CatalogC.p11,
|
|
||||||
CatalogC.p12)
|
|
||||||
|
|
||||||
|
|
||||||
class CatalogTests(unittest.TestCase):
|
class CatalogTests(unittest.TestCase):
|
||||||
|
@ -143,7 +146,7 @@ class CatalogTests(unittest.TestCase):
|
||||||
self.assertRaises(di.Error, CatalogC.get, 'undefined')
|
self.assertRaises(di.Error, CatalogC.get, 'undefined')
|
||||||
|
|
||||||
def test_has(self):
|
def test_has(self):
|
||||||
"""Test checks of providers availability in subsets."""
|
"""Test checks of providers availability in catalog."""
|
||||||
self.assertTrue(CatalogC.has('p11'))
|
self.assertTrue(CatalogC.has('p11'))
|
||||||
self.assertTrue(CatalogC.has('p12'))
|
self.assertTrue(CatalogC.has('p12'))
|
||||||
self.assertTrue(CatalogC.has('p21'))
|
self.assertTrue(CatalogC.has('p21'))
|
||||||
|
@ -152,18 +155,6 @@ class CatalogTests(unittest.TestCase):
|
||||||
self.assertTrue(CatalogC.has('p32'))
|
self.assertTrue(CatalogC.has('p32'))
|
||||||
self.assertFalse(CatalogC.has('undefined'))
|
self.assertFalse(CatalogC.has('undefined'))
|
||||||
|
|
||||||
def test_is_subset_owner(self):
|
|
||||||
"""Test that catalog is subset owner."""
|
|
||||||
subset1 = CatalogA.subset('p11')
|
|
||||||
self.assertTrue(CatalogA.is_subset_owner(subset1))
|
|
||||||
self.assertFalse(CatalogB.is_subset_owner(subset1))
|
|
||||||
self.assertFalse(CatalogC.is_subset_owner(subset1))
|
|
||||||
|
|
||||||
subset2 = di.Subset(CatalogA.p11)
|
|
||||||
self.assertTrue(CatalogA.is_subset_owner(subset2))
|
|
||||||
self.assertFalse(CatalogB.is_subset_owner(subset2))
|
|
||||||
self.assertFalse(CatalogC.is_subset_owner(subset2))
|
|
||||||
|
|
||||||
def test_filter_all_providers_by_type(self):
|
def test_filter_all_providers_by_type(self):
|
||||||
"""Test getting of all catalog providers of specific type."""
|
"""Test getting of all catalog providers of specific type."""
|
||||||
self.assertTrue(len(CatalogC.filter(di.Provider)) == 6)
|
self.assertTrue(len(CatalogC.filter(di.Provider)) == 6)
|
||||||
|
|
|
@ -208,28 +208,3 @@ class IsCatalogTests(unittest.TestCase):
|
||||||
def test_with_object(self):
|
def test_with_object(self):
|
||||||
"""Test with object."""
|
"""Test with object."""
|
||||||
self.assertFalse(di.is_catalog(object()))
|
self.assertFalse(di.is_catalog(object()))
|
||||||
|
|
||||||
|
|
||||||
class IsCatalogSubsetTests(unittest.TestCase):
|
|
||||||
"""`is_catalog_subset()` test cases."""
|
|
||||||
|
|
||||||
def test_with_cls(self):
|
|
||||||
"""Test with class."""
|
|
||||||
self.assertFalse(di.is_catalog_subset(di.Subset))
|
|
||||||
|
|
||||||
def test_with_instance(self):
|
|
||||||
"""Test with class."""
|
|
||||||
class Catalog(di.AbstractCatalog):
|
|
||||||
"""Example catalog child class."""
|
|
||||||
|
|
||||||
p1 = di.Provider()
|
|
||||||
|
|
||||||
self.assertTrue(di.is_catalog_subset(di.Subset(Catalog.p1)))
|
|
||||||
|
|
||||||
def test_with_string(self):
|
|
||||||
"""Test with string."""
|
|
||||||
self.assertFalse(di.is_catalog_subset('some_string'))
|
|
||||||
|
|
||||||
def test_with_object(self):
|
|
||||||
"""Test with object."""
|
|
||||||
self.assertFalse(di.is_catalog_subset(object()))
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user