mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-25 11:04:01 +03:00
Add catalog packs
This commit is contained in:
parent
3b54763563
commit
730d46f9a0
|
@ -1,7 +1,6 @@
|
|||
"""Dependency injector."""
|
||||
|
||||
from .catalog import AbstractCatalog
|
||||
from .catalog import Subset
|
||||
from .catalog import override
|
||||
|
||||
from .providers import Provider
|
||||
|
@ -31,7 +30,6 @@ from .utils import is_kwarg_injection
|
|||
from .utils import is_attribute_injection
|
||||
from .utils import is_method_injection
|
||||
from .utils import is_catalog
|
||||
from .utils import is_catalog_subset
|
||||
|
||||
from .errors import Error
|
||||
|
||||
|
@ -39,7 +37,6 @@ from .errors import Error
|
|||
__all__ = (
|
||||
# Catalogs
|
||||
'AbstractCatalog',
|
||||
'Subset',
|
||||
'override',
|
||||
|
||||
# Providers
|
||||
|
@ -72,7 +69,6 @@ __all__ = (
|
|||
'is_attribute_injection',
|
||||
'is_method_injection',
|
||||
'is_catalog',
|
||||
'is_catalog_subset',
|
||||
|
||||
# Errors
|
||||
'Error',
|
||||
|
|
|
@ -8,6 +8,67 @@ from .utils import is_provider
|
|||
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):
|
||||
"""Providers catalog meta class."""
|
||||
|
||||
|
@ -26,12 +87,14 @@ class CatalogMetaClass(type):
|
|||
providers.update(cls_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.cls_providers = cls_providers
|
||||
cls.inherited_providers = inherited_providers
|
||||
cls.providers = providers
|
||||
|
||||
cls.Pack = CatalogPackFactory(cls)
|
||||
|
||||
for name, provider in six.iteritems(cls_providers):
|
||||
provider.bind = ProviderBinding(cls, name)
|
||||
|
||||
|
@ -56,28 +119,19 @@ class AbstractCatalog(object):
|
|||
:type inherited_providers: dict[str, dependency_injector.Provider]
|
||||
:param inherited_providers: Dict of providers, that are inherited from
|
||||
parent catalogs
|
||||
|
||||
:type Pack: CatalogPack
|
||||
:param Pack: Catalog's pack class
|
||||
"""
|
||||
|
||||
providers = dict()
|
||||
Pack = CatalogPackFactory
|
||||
|
||||
cls_providers = dict()
|
||||
inherited_providers = dict()
|
||||
providers = dict()
|
||||
|
||||
__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
|
||||
def filter(cls, provider_type):
|
||||
"""Return dict of providers, that are instance of provided type."""
|
||||
|
@ -120,64 +174,6 @@ class ProviderBinding(object):
|
|||
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):
|
||||
"""Catalog overriding decorator."""
|
||||
def decorator(overriding_catalog):
|
||||
|
|
|
@ -62,12 +62,6 @@ def is_catalog(instance):
|
|||
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):
|
||||
"""Return dictionary of kwargs, patched with injections."""
|
||||
init_kwargs = dict(((injection.name, injection.value)
|
||||
|
|
|
@ -35,12 +35,11 @@ class AuthView(BaseWebView):
|
|||
class PhotosView(BaseWebView):
|
||||
"""Example photo processing web view."""
|
||||
|
||||
# Creating example views with appropriate service provider subsets:
|
||||
auth_view = AuthView(di.Subset(Services.users,
|
||||
Services.auth))
|
||||
photos_view = PhotosView(di.Subset(Services.users,
|
||||
Services.photos))
|
||||
|
||||
# Creating example views with appropriate service provider packs:
|
||||
auth_view = AuthView(Services.Pack(Services.users,
|
||||
Services.auth))
|
||||
photos_view = PhotosView(Services.Pack(Services.users,
|
||||
Services.photos))
|
||||
|
||||
# Making some asserts:
|
||||
assert auth_view.services.users is Services.users
|
|
@ -25,6 +25,13 @@ class CatalogC(CatalogB):
|
|||
p32 = di.Provider()
|
||||
|
||||
|
||||
class CatalogD(di.AbstractCatalog):
|
||||
"""Test catalog D."""
|
||||
|
||||
p11 = di.Provider()
|
||||
p12 = di.Provider()
|
||||
|
||||
|
||||
class CatalogsInheritanceTests(unittest.TestCase):
|
||||
"""Catalogs inheritance tests."""
|
||||
|
||||
|
@ -71,59 +78,55 @@ class CatalogsInheritanceTests(unittest.TestCase):
|
|||
p32=CatalogC.p32))
|
||||
|
||||
|
||||
class _BasicSubsetTests(object):
|
||||
"""Catalog subset test cases."""
|
||||
class CatalogPackTests(unittest.TestCase):
|
||||
"""Catalog pack test cases."""
|
||||
|
||||
def test_get_attr_from_subset(self):
|
||||
"""Test get providers (attribute) from subset."""
|
||||
self.assertIs(self.subset.p11, CatalogC.p11)
|
||||
self.assertIs(self.subset.p12, CatalogC.p12)
|
||||
def setUp(self):
|
||||
"""Set test environment up."""
|
||||
self.pack = CatalogC.Pack(CatalogC.p11,
|
||||
CatalogC.p12)
|
||||
|
||||
def test_get_attr_not_from_subset(self):
|
||||
"""Test get providers (attribute) that are not in subset."""
|
||||
self.assertRaises(di.Error, getattr, self.subset, 'p21')
|
||||
self.assertRaises(di.Error, getattr, self.subset, 'p22')
|
||||
self.assertRaises(di.Error, getattr, self.subset, 'p31')
|
||||
self.assertRaises(di.Error, getattr, self.subset, 'p32')
|
||||
def test_get_attr_from_pack(self):
|
||||
"""Test get providers (attribute) from pack."""
|
||||
self.assertIs(self.pack.p11, CatalogC.p11)
|
||||
self.assertIs(self.pack.p12, CatalogC.p12)
|
||||
|
||||
def test_get_method_from_subset(self):
|
||||
"""Test get providers (get() method) from subset."""
|
||||
self.assertIs(self.subset.get('p11'), CatalogC.p11)
|
||||
self.assertIs(self.subset.get('p12'), CatalogC.p12)
|
||||
def test_get_attr_not_from_pack(self):
|
||||
"""Test get providers (attribute) that are not in pack."""
|
||||
self.assertRaises(di.Error, getattr, self.pack, 'p21')
|
||||
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):
|
||||
"""Test get providers (get() method) that are not in subset."""
|
||||
self.assertRaises(di.Error, self.subset.get, 'p21')
|
||||
self.assertRaises(di.Error, self.subset.get, 'p22')
|
||||
self.assertRaises(di.Error, self.subset.get, 'p31')
|
||||
self.assertRaises(di.Error, self.subset.get, 'p32')
|
||||
def test_get_method_from_pack(self):
|
||||
"""Test get providers (get() method) from pack."""
|
||||
self.assertIs(self.pack.get('p11'), CatalogC.p11)
|
||||
self.assertIs(self.pack.get('p12'), CatalogC.p12)
|
||||
|
||||
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):
|
||||
"""Test checks of providers availability in subsets."""
|
||||
self.assertTrue(self.subset.has('p11'))
|
||||
self.assertTrue(self.subset.has('p12'))
|
||||
"""Test checks of providers availability in pack."""
|
||||
self.assertTrue(self.pack.has('p11'))
|
||||
self.assertTrue(self.pack.has('p12'))
|
||||
|
||||
self.assertFalse(self.subset.has('p21'))
|
||||
self.assertFalse(self.subset.has('p22'))
|
||||
self.assertFalse(self.subset.has('p31'))
|
||||
self.assertFalse(self.subset.has('p32'))
|
||||
self.assertFalse(self.pack.has('p21'))
|
||||
self.assertFalse(self.pack.has('p22'))
|
||||
self.assertFalse(self.pack.has('p31'))
|
||||
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):
|
||||
"""Subset, that is created by catalog factory method, tests."""
|
||||
|
||||
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)
|
||||
def test_create_pack_with_unbound_provider(self):
|
||||
"""Test that pack is not created with unbound provider."""
|
||||
self.assertRaises(di.Error, CatalogC.Pack, di.Provider())
|
||||
|
||||
|
||||
class CatalogTests(unittest.TestCase):
|
||||
|
@ -143,7 +146,7 @@ class CatalogTests(unittest.TestCase):
|
|||
self.assertRaises(di.Error, CatalogC.get, 'undefined')
|
||||
|
||||
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('p12'))
|
||||
self.assertTrue(CatalogC.has('p21'))
|
||||
|
@ -152,18 +155,6 @@ class CatalogTests(unittest.TestCase):
|
|||
self.assertTrue(CatalogC.has('p32'))
|
||||
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):
|
||||
"""Test getting of all catalog providers of specific type."""
|
||||
self.assertTrue(len(CatalogC.filter(di.Provider)) == 6)
|
||||
|
|
|
@ -208,28 +208,3 @@ class IsCatalogTests(unittest.TestCase):
|
|||
def test_with_object(self):
|
||||
"""Test with 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