mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-21 17:16:46 +03:00
0.0.4 version
This commit is contained in:
parent
028e5e9ef7
commit
143ba92b0a
139
README.md
139
README.md
|
@ -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()
|
||||
```
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
56
examples/overrides.py
Normal 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()
|
|
@ -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']
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user