Add catalog packs

This commit is contained in:
Roman Mogilatov 2015-10-15 20:15:38 +03:00
parent 3b54763563
commit 730d46f9a0
6 changed files with 126 additions and 175 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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