Refactor catalog subsets

This commit is contained in:
Roman Mogilatov 2015-10-14 17:51:05 +03:00
parent e1d6583157
commit 2ed460f054
5 changed files with 68 additions and 32 deletions

View File

@ -1,7 +1,7 @@
"""Dependency injector.""" """Dependency injector."""
from .catalog import AbstractCatalog from .catalog import AbstractCatalog
from .catalog import CatalogSubset from .catalog import Subset
from .catalog import override from .catalog import override
from .providers import Provider from .providers import Provider
@ -39,7 +39,7 @@ from .errors import Error
__all__ = ( __all__ = (
# Catalogs # Catalogs
'AbstractCatalog', 'AbstractCatalog',
'CatalogSubset', 'Subset',
'override', 'override',
# Providers # Providers

View File

@ -29,7 +29,13 @@ class CatalogMetaClass(type):
attributes['cls_providers'] = cls_providers attributes['cls_providers'] = cls_providers
attributes['inherited_providers'] = inherited_providers attributes['inherited_providers'] = inherited_providers
attributes['providers'] = providers attributes['providers'] = providers
return type.__new__(mcs, class_name, bases, attributes)
cls = type.__new__(mcs, class_name, bases, attributes)
for name, provider in six.iteritems(cls_providers):
provider.bind = ProviderBinding(cls, name)
return cls
def __repr__(cls): def __repr__(cls):
"""Return string representation of the catalog class.""" """Return string representation of the catalog class."""
@ -58,14 +64,15 @@ class AbstractCatalog(object):
__IS_CATALOG__ = True __IS_CATALOG__ = True
def __new__(cls, *providers): @classmethod
"""Catalog constructor. def subset(cls, *provider_names):
"""Catalog subset factory.
Catalogs are declaratives entities that could not be instantiated. Create subset of catalog providers using provider names.
Catalog constructor is designed to produce subsets of catalog
providers.
""" """
return CatalogSubset(catalog=cls, providers=providers) return Subset(*(provider
for name, provider in six.iteritems(cls.providers)
if name in provider_names))
@classmethod @classmethod
def is_subset_owner(cls, subset): def is_subset_owner(cls, subset):
@ -103,28 +110,42 @@ class AbstractCatalog(object):
return name in cls.providers return name in cls.providers
class CatalogSubset(object): class ProviderBinding(object):
"""Catalog provider binding."""
__slots__ = ('catalog', 'name')
def __init__(self, catalog, name):
"""Initializer."""
self.catalog = catalog
self.name = name
class Subset(object):
"""Subset of catalog providers.""" """Subset of catalog providers."""
__IS_SUBSET__ = True __IS_SUBSET__ = True
__slots__ = ('catalog', 'available_providers', 'providers', '__dict__') __slots__ = ('catalog', 'providers', '__dict__')
def __init__(self, catalog, providers): def __init__(self, *providers):
"""Initializer.""" """Initializer."""
self.catalog = catalog if not providers:
self.available_providers = set(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() self.providers = dict()
for provider_name in self.available_providers: for provider in providers:
try: provider_bind = self._get_provider_binding(provider)
provider = self.catalog.providers[provider_name] if not self.catalog.get(provider_bind.name) is provider:
except KeyError: raise Error('Subset can contain providers from '
raise Error('Subset could not add "{0}" provider in scope, ' 'one catalog {0}, '
'because {1} has no provider with ' 'unknown provider - {1}'.format(self.catalog,
'such name'.format(provider_name, self.catalog)) provider))
else: self.providers[provider_bind.name] = provider
self.providers[provider_name] = provider self.__dict__[provider_bind.name] = provider
self.__dict__.update(self.providers) super(Subset, self).__init__()
super(CatalogSubset, self).__init__()
def get(self, name): def get(self, name):
"""Return provider with specified name or raises error.""" """Return provider with specified name or raises error."""
@ -144,7 +165,14 @@ class CatalogSubset(object):
def __repr__(self): def __repr__(self):
"""Return string representation of subset.""" """Return string representation of subset."""
return '<Subset ({0}), {1}>'.format( return '<Subset ({0}), {1}>'.format(
', '.join(self.available_providers), self.catalog) ', '.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): def _raise_undefined_provider_error(self, name):
"""Raise error for cases when there is no such provider in subset.""" """Raise error for cases when there is no such provider in subset."""

View File

@ -18,11 +18,12 @@ class Provider(object):
"""Base provider class.""" """Base provider class."""
__IS_PROVIDER__ = True __IS_PROVIDER__ = True
__slots__ = ('overridden_by',) __slots__ = ('overridden_by', 'bind')
def __init__(self): def __init__(self):
"""Initializer.""" """Initializer."""
self.overridden_by = None self.overridden_by = None
self.bind = None
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
"""Return provided instance.""" """Return provided instance."""

View File

@ -36,8 +36,11 @@ 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 subsets:
auth_view = AuthView(Services('users', 'auth')) auth_view = AuthView(di.Subset(Services.users,
photos_view = PhotosView(Services('users', 'photos')) Services.auth))
photos_view = PhotosView(di.Subset(Services.users,
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

@ -215,12 +215,16 @@ class IsCatalogSubsetTests(unittest.TestCase):
def test_with_cls(self): def test_with_cls(self):
"""Test with class.""" """Test with class."""
self.assertFalse(di.is_catalog_subset(di.CatalogSubset)) self.assertFalse(di.is_catalog_subset(di.Subset))
def test_with_instance(self): def test_with_instance(self):
"""Test with class.""" """Test with class."""
self.assertTrue(di.is_catalog_subset( class Catalog(di.AbstractCatalog):
di.CatalogSubset(catalog=di.AbstractCatalog, providers=tuple()))) """Example catalog child class."""
p1 = di.Provider()
self.assertTrue(di.is_catalog_subset(di.Subset(Catalog.p1)))
def test_with_string(self): def test_with_string(self):
"""Test with string.""" """Test with string."""