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.""" """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',

View File

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

View File

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

View File

@ -35,12 +35,11 @@ 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

View File

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

View File

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