From 730d46f9a0f5dd7d59d42fb10e3a186c1985f7f4 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Thu, 15 Oct 2015 20:15:38 +0300 Subject: [PATCH] Add catalog packs --- dependency_injector/__init__.py | 4 - dependency_injector/catalog.py | 150 ++++++++++----------- dependency_injector/utils.py | 6 - examples/catalogs/{subsets.py => packs.py} | 11 +- tests/test_catalog.py | 105 +++++++-------- tests/test_utils.py | 25 ---- 6 files changed, 126 insertions(+), 175 deletions(-) rename examples/catalogs/{subsets.py => packs.py} (83%) diff --git a/dependency_injector/__init__.py b/dependency_injector/__init__.py index 8b7bb3a0..8420bd47 100644 --- a/dependency_injector/__init__.py +++ b/dependency_injector/__init__.py @@ -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', diff --git a/dependency_injector/catalog.py b/dependency_injector/catalog.py index f7f543ea..f2ca1136 100644 --- a/dependency_injector/catalog.py +++ b/dependency_injector/catalog.py @@ -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 ''.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 ''.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): diff --git a/dependency_injector/utils.py b/dependency_injector/utils.py index 426584cd..42a18202 100644 --- a/dependency_injector/utils.py +++ b/dependency_injector/utils.py @@ -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) diff --git a/examples/catalogs/subsets.py b/examples/catalogs/packs.py similarity index 83% rename from examples/catalogs/subsets.py rename to examples/catalogs/packs.py index 84206699..88720ee9 100644 --- a/examples/catalogs/subsets.py +++ b/examples/catalogs/packs.py @@ -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 diff --git a/tests/test_catalog.py b/tests/test_catalog.py index a1da8f21..8572f367 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -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) diff --git a/tests/test_utils.py b/tests/test_utils.py index 3835a76b..de27bab1 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -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()))