0.0.4 version

This commit is contained in:
Roman Mogilatov 2015-01-11 18:10:11 +02:00
parent 028e5e9ef7
commit 143ba92b0a
9 changed files with 263 additions and 195 deletions

139
README.md
View File

@ -48,36 +48,75 @@ class AppCatalog(Catalog):
""" :type: (objects.Provider) -> ObjectB """
# Catalog injection into consumer class.
class Consumer(object):
catalog = AppCatalog(AppCatalog.object_a,
AppCatalog.object_b)
def return_a_b(self):
return (self.catalog.object_a(),
self.catalog.object_b())
a1, b1 = Consumer().return_a_b()
# Catalog static provides.
a2 = AppCatalog.object_a()
b2 = AppCatalog.object_b()
a1, a2 = AppCatalog.object_a(), AppCatalog.object_a()
b1, b2 = AppCatalog.object_b(), AppCatalog.object_b()
# Some asserts.
assert a1 is not a2
assert b1 is not b2
assert a1.db is a2.db is b1.db is b2.db
assert a1.db is a2.db is b1.db is b2.db is AppCatalog.database()
# Dependency injection (The Python Way) into class.
class Consumer(object):
dependencies = AppCatalog(AppCatalog.object_a,
AppCatalog.object_b)
def test(self):
a1 = self.dependencies.object_a()
a2 = self.dependencies.object_a()
b1 = self.dependencies.object_b()
b2 = self.dependencies.object_b()
# Some asserts.
assert a1 is not a2
assert b1 is not b2
assert a1.db is a2.db is b1.db is b2.db
try:
self.dependencies.database()
except AttributeError:
pass
else:
raise Exception('Database is not listed as a dependency')
Consumer().test()
# Dependency injection (The Python Way) into a callback.
def consumer_callback(dependencies=AppCatalog(AppCatalog.object_a,
AppCatalog.object_b)):
a1 = dependencies.object_a()
a2 = dependencies.object_a()
b1 = dependencies.object_b()
b2 = dependencies.object_b()
# Some asserts.
assert a1 is not a2
assert b1 is not b2
assert a1.db is a2.db is b1.db is b2.db
try:
dependencies.database()
except AttributeError:
pass
else:
raise Exception('Database is not listed as a dependency')
```
Example of injections using objects.catalog:
Example of overriding object providers:
```python
"""
Concept example of objects injections.
Concept example of objects overrides.
"""
from objects import Catalog, Singleton, NewInstance, InitArg, Attribute, inject
from objects import Catalog, Singleton, NewInstance, InitArg, Attribute, overrides
import sqlite3
@ -87,6 +126,10 @@ class ObjectA(object):
self.db = db
class ObjectAMock(ObjectA):
pass
# Catalog of objects providers.
class AppCatalog(Catalog):
"""
@ -103,57 +146,25 @@ class AppCatalog(Catalog):
""" :type: (objects.Provider) -> ObjectA """
# Class attributes injections.
@inject(Attribute('a', AppCatalog.object_a))
@inject(Attribute('database', AppCatalog.database))
class Consumer(object):
# Overriding AppCatalog by SandboxCatalog with some mocks.
@overrides(AppCatalog)
class SandboxCatalog(AppCatalog):
"""
Some consumer class with database dependency via attribute.
Sandbox objects catalog with some mocks.
"""
a = None
object_a = NewInstance(ObjectAMock,
InitArg('db', AppCatalog.database))
""" :type: (objects.Provider) -> ObjectA """
database = None
""" :type: (objects.Provider) -> sqlite3.Connection """
def tests(self):
a1, a2 = self.a(), self.a()
# Catalog static provides.
a1 = AppCatalog.object_a()
a2 = AppCatalog.object_a()
assert a1 is not a2
assert a1.db is a2.db is self.database()
consumer = Consumer()
consumer.tests()
# Class __init__ injections.
@inject(InitArg('a1', AppCatalog.object_a))
@inject(InitArg('a2', AppCatalog.object_a))
@inject(InitArg('database', AppCatalog.database))
class ConsumerWithInitArg(object):
"""
Some consumer class with database dependency via init arg.
"""
def __init__(self, a1, a2, database):
"""
Initializer.
:param a1: ObjectA
:param a2: ObjectA
:param database: sqlite3.Connection
"""
self.a1 = a1
self.a2 = a2
self.database = database
def tests(self):
assert self.a1 is not self.a2
assert self.a1.db is self.a2.db is self.database
consumer = ConsumerWithInitArg()
consumer.tests()
# Some asserts.
assert isinstance(a1, ObjectAMock)
assert isinstance(a2, ObjectAMock)
assert a1 is not a2
assert a1.db is a2.db is AppCatalog.database()
```

View File

@ -1 +1 @@
0.0.3
0.0.4

View File

@ -39,23 +39,61 @@ class AppCatalog(Catalog):
""" :type: (objects.Provider) -> ObjectB """
# Catalog injection into consumer class.
class Consumer(object):
catalog = AppCatalog(AppCatalog.object_a,
AppCatalog.object_b)
def return_a_b(self):
return (self.catalog.object_a(),
self.catalog.object_b())
a1, b1 = Consumer().return_a_b()
# Catalog static provides.
a2 = AppCatalog.object_a()
b2 = AppCatalog.object_b()
a1, a2 = AppCatalog.object_a(), AppCatalog.object_a()
b1, b2 = AppCatalog.object_b(), AppCatalog.object_b()
# Some asserts.
assert a1 is not a2
assert b1 is not b2
assert a1.db is a2.db is b1.db is b2.db
assert a1.db is a2.db is b1.db is b2.db is AppCatalog.database()
# Dependency injection (The Python Way) into class.
class Consumer(object):
dependencies = AppCatalog(AppCatalog.object_a,
AppCatalog.object_b)
def test(self):
a1 = self.dependencies.object_a()
a2 = self.dependencies.object_a()
b1 = self.dependencies.object_b()
b2 = self.dependencies.object_b()
# Some asserts.
assert a1 is not a2
assert b1 is not b2
assert a1.db is a2.db is b1.db is b2.db
try:
self.dependencies.database()
except AttributeError:
pass
else:
raise Exception('Database is not listed as a dependency')
Consumer().test()
# Dependency injection (The Python Way) into a callback.
def consumer_callback(dependencies=AppCatalog(AppCatalog.object_a,
AppCatalog.object_b)):
a1 = dependencies.object_a()
a2 = dependencies.object_a()
b1 = dependencies.object_b()
b2 = dependencies.object_b()
# Some asserts.
assert a1 is not a2
assert b1 is not b2
assert a1.db is a2.db is b1.db is b2.db
try:
dependencies.database()
except AttributeError:
pass
else:
raise Exception('Database is not listed as a dependency')

View File

@ -1,83 +0,0 @@
"""
Concept example of objects injections.
"""
from objects import Catalog, Singleton, NewInstance, InitArg, Attribute, inject
import sqlite3
# Some example class.
class ObjectA(object):
def __init__(self, db):
self.db = db
# Catalog of objects providers.
class AppCatalog(Catalog):
"""
Objects catalog.
"""
database = Singleton(sqlite3.Connection,
InitArg('database', ':memory:'),
Attribute('row_factory', sqlite3.Row))
""" :type: (objects.Provider) -> sqlite3.Connection """
object_a = NewInstance(ObjectA,
InitArg('db', database))
""" :type: (objects.Provider) -> ObjectA """
# Class attributes injections.
@inject(Attribute('a', AppCatalog.object_a))
@inject(Attribute('database', AppCatalog.database))
class Consumer(object):
"""
Some consumer class with database dependency via attribute.
"""
a = None
""" :type: (objects.Provider) -> ObjectA """
database = None
""" :type: (objects.Provider) -> sqlite3.Connection """
def tests(self):
a1, a2 = self.a(), self.a()
assert a1 is not a2
assert a1.db is a2.db is self.database()
consumer = Consumer()
consumer.tests()
# Class __init__ injections.
@inject(InitArg('a1', AppCatalog.object_a))
@inject(InitArg('a2', AppCatalog.object_a))
@inject(InitArg('database', AppCatalog.database))
class ConsumerWithInitArg(object):
"""
Some consumer class with database dependency via init arg.
"""
def __init__(self, a1, a2, database):
"""
Initializer.
:param a1: ObjectA
:param a2: ObjectA
:param database: sqlite3.Connection
"""
self.a1 = a1
self.a2 = a2
self.database = database
def tests(self):
assert self.a1 is not self.a2
assert self.a1.db is self.a2.db is self.database
consumer = ConsumerWithInitArg()
consumer.tests()

56
examples/overrides.py Normal file
View File

@ -0,0 +1,56 @@
"""
Concept example of objects overrides.
"""
from objects import Catalog, Singleton, NewInstance, InitArg, Attribute, overrides
import sqlite3
# Some example class.
class ObjectA(object):
def __init__(self, db):
self.db = db
class ObjectAMock(ObjectA):
pass
# Catalog of objects providers.
class AppCatalog(Catalog):
"""
Objects catalog.
"""
database = Singleton(sqlite3.Connection,
InitArg('database', ':memory:'),
Attribute('row_factory', sqlite3.Row))
""" :type: (objects.Provider) -> sqlite3.Connection """
object_a = NewInstance(ObjectA,
InitArg('db', database))
""" :type: (objects.Provider) -> ObjectA """
# Overriding AppCatalog by SandboxCatalog with some mocks.
@overrides(AppCatalog)
class SandboxCatalog(AppCatalog):
"""
Sandbox objects catalog with some mocks.
"""
object_a = NewInstance(ObjectAMock,
InitArg('db', AppCatalog.database))
""" :type: (objects.Provider) -> ObjectA """
# Catalog static provides.
a1 = AppCatalog.object_a()
a2 = AppCatalog.object_a()
# Some asserts.
assert isinstance(a1, ObjectAMock)
assert isinstance(a2, ObjectAMock)
assert a1 is not a2
assert a1.db is a2.db is AppCatalog.database()

View File

@ -2,17 +2,17 @@
`Objects` library.
"""
from .catalog import Catalog
from .catalog import Catalog, overrides
from .providers import (Provider, NewInstance, Singleton, Class, Object,
Function, Value)
from .injections import InitArg, Attribute, Method, inject
from .injections import InitArg, Attribute, Method, uses
__all__ = ['Catalog',
__all__ = ['Catalog', 'overrides',
# Providers
'Provider', 'NewInstance', 'Singleton', 'Class',
'Object', 'Function', 'Value',
# Injections
'InitArg', 'Attribute', 'Method', 'inject']
'InitArg', 'Attribute', 'Method', 'uses']

View File

@ -14,19 +14,58 @@ class Catalog(object):
"""
Initializer.
"""
self._clean_unused_providers(used_providers)
self.__used_providers__ = set(used_providers)
def _clean_unused_providers(self, used_providers):
def __getattribute__(self, item):
"""
Sets every catalog's provider in None except of `used_providers` list.
Returns providers.
:param list|tuple|set used_providers:
:param item:
:return:
"""
used_providers = set(used_providers)
for attribute_name in set(dir(self.__class__)) - set(dir(Catalog)):
provider = getattr(self, attribute_name)
attribute = super(Catalog, self).__getattribute__(item)
if item in ('__used_providers__',):
return attribute
if attribute not in self.__used_providers__:
raise AttributeError('Provider \'{}\' is not listed in '
'dependencies'.format(item))
return attribute
@classmethod
def __all_providers__(cls):
"""
Returns set of all class providers.
"""
providers = set()
for attr_name in set(dir(cls)) - set(dir(Catalog)):
provider = getattr(cls, attr_name)
if not isinstance(provider, Provider):
continue
if provider not in used_providers:
setattr(self, attribute_name, None)
providers.add((attr_name, provider))
return providers
@classmethod
def __override___(cls, overriding):
"""
Overrides current catalog providers by overriding catalog providers.
:param overriding: Catalog
"""
overriden = overriding.__all_providers__() - cls.__all_providers__()
for name, provider in overriden:
overridden_provider = getattr(cls, name)
overridden_provider.__override__(provider)
def overrides(catalog):
"""
Catalog overriding decorator.
:param catalog:
:return:
"""
def decorator(overriding_catalog):
catalog.__override___(overriding_catalog)
return overriding_catalog
return decorator

View File

@ -46,25 +46,12 @@ class Method(Injection):
"""
def inject(injection):
def uses(provider):
"""
Injection decorator.
Providers usage decorator.
"""
def decorator(callback_or_cls):
if isclass(callback_or_cls):
cls = callback_or_cls
if isinstance(injection, Attribute):
setattr(cls, injection.name, injection.injectable)
elif isinstance(injection, InitArg):
cls.__init__ = decorator(cls.__init__)
return cls
else:
callback = callback_or_cls
@wraps(callback)
def wrapped(*args, **kwargs):
if injection.name not in kwargs:
kwargs[injection.name] = injection.value
return callback(*args, **kwargs)
return wrapped
def decorator(cls):
catalog = getattr(cls, 'catalog')
# catalog.__add__provider__(provider)
return cls
return decorator

View File

@ -11,6 +11,13 @@ class Provider(object):
"""
__is_objects_provider__ = True
__overridden_by__ = list()
def __init__(self):
"""
Initializer.
"""
self.__overridden_by__ = list()
def __call__(self, *args, **kwargs):
"""
@ -18,6 +25,12 @@ class Provider(object):
"""
raise NotImplementedError()
def __override__(self, provider):
"""
Overrides provider with another provider.
"""
self.__overridden_by__.append(provider)
def prepare_injections(injections):
"""
@ -48,11 +61,15 @@ class NewInstance(Provider):
self.init_injections = fetch_injections(injections, InitArg)
self.attribute_injections = fetch_injections(injections, Attribute)
self.method_injections = fetch_injections(injections, Method)
super(NewInstance, self).__init__()
def __call__(self, *args, **kwargs):
"""
Returns provided instance.
"""
if self.__overridden_by__:
return self.__overridden_by__[-1].__call__(*args, **kwargs)
init_injections = prepare_injections(self.init_injections)
init_injections = dict(init_injections)
init_injections.update(kwargs)
@ -102,11 +119,14 @@ class _StaticProvider(Provider):
Initializer.
"""
self.provides = provides
super(_StaticProvider, self).__init__()
def __call__(self):
"""
Returns provided instance.
"""
if self.__overridden_by__:
return self.__overridden_by__[-1].__call__()
return self.provides