Renaming InitArg to KwArg, adding @inject decorator

This commit is contained in:
Roman Mogilatov 2015-03-23 01:04:18 +02:00
parent 6bbf7e3526
commit 1032143f5b
17 changed files with 167 additions and 96 deletions

View File

@ -12,15 +12,16 @@ Python catalogs of objects providers
Example:
```python
"""Concept example of Objects."""
"""Concept example of `Objects`."""
from objects import AbstractCatalog
from objects.catalog import AbstractCatalog
from objects.providers import Singleton
from objects.providers import NewInstance
from objects.injections import InitArg
from objects.injections import KwArg
from objects.injections import Attribute
from objects.injections import inject
import sqlite3
@ -49,17 +50,17 @@ class Catalog(AbstractCatalog):
"""Catalog of objects providers."""
database = Singleton(sqlite3.Connection,
InitArg('database', ':memory:'),
KwArg('database', ':memory:'),
Attribute('row_factory', sqlite3.Row))
""":type: (objects.Provider) -> sqlite3.Connection"""
object_a = NewInstance(ObjectA,
InitArg('db', database))
KwArg('db', database))
""":type: (objects.Provider) -> ObjectA"""
object_b = NewInstance(ObjectB,
InitArg('a', object_a),
InitArg('db', database))
KwArg('a', object_a),
KwArg('db', database))
""":type: (objects.Provider) -> ObjectB"""
@ -67,8 +68,18 @@ class Catalog(AbstractCatalog):
a1, a2 = Catalog.object_a(), Catalog.object_a()
b1, b2 = Catalog.object_b(), Catalog.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 is Catalog.database()
# Example of inline injections.
@inject(KwArg('a', Catalog.object_a))
@inject(KwArg('b', Catalog.object_b))
@inject(KwArg('database', Catalog.database))
def example(a, b, database):
assert a.db is b.db is database is Catalog.database()
example()
```

View File

@ -1,13 +1,12 @@
"""Callable provider examples."""
from objects import AbstractCatalog
from objects.catalog import AbstractCatalog
from objects.providers import Singleton
from objects.providers import Callable
from objects.injections import InitArg
from objects.injections import KwArg
from objects.injections import Attribute
from objects.injections import Injection
import sqlite3
@ -22,12 +21,12 @@ class Catalog(AbstractCatalog):
"""Catalog of objects providers."""
database = Singleton(sqlite3.Connection,
InitArg('database', ':memory:'),
KwArg('database', ':memory:'),
Attribute('row_factory', sqlite3.Row))
""":type: (objects.Provider) -> sqlite3.Connection"""
consuming_function = Callable(consuming_function,
Injection('db', database))
KwArg('db', database))
""":type: (objects.Provider) -> consuming_function"""

View File

@ -1,12 +1,13 @@
"""Concept example of `Objects`."""
from objects import AbstractCatalog
from objects.catalog import AbstractCatalog
from objects.providers import Singleton
from objects.providers import NewInstance
from objects.injections import InitArg
from objects.injections import KwArg
from objects.injections import Attribute
from objects.injections import inject
import sqlite3
@ -35,17 +36,17 @@ class Catalog(AbstractCatalog):
"""Catalog of objects providers."""
database = Singleton(sqlite3.Connection,
InitArg('database', ':memory:'),
KwArg('database', ':memory:'),
Attribute('row_factory', sqlite3.Row))
""":type: (objects.Provider) -> sqlite3.Connection"""
object_a = NewInstance(ObjectA,
InitArg('db', database))
KwArg('db', database))
""":type: (objects.Provider) -> ObjectA"""
object_b = NewInstance(ObjectB,
InitArg('a', object_a),
InitArg('db', database))
KwArg('a', object_a),
KwArg('db', database))
""":type: (objects.Provider) -> ObjectB"""
@ -53,7 +54,17 @@ class Catalog(AbstractCatalog):
a1, a2 = Catalog.object_a(), Catalog.object_a()
b1, b2 = Catalog.object_b(), Catalog.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 is Catalog.database()
# Example of inline injections.
@inject(KwArg('a', Catalog.object_a))
@inject(KwArg('b', Catalog.object_b))
@inject(KwArg('database', Catalog.database))
def example(a, b, database):
assert a.db is b.db is database is Catalog.database()
example()

View File

@ -1,11 +1,11 @@
"""Config provider examples."""
from objects import AbstractCatalog
from objects.catalog import AbstractCatalog
from objects.providers import Config
from objects.providers import NewInstance
from objects.injections import InitArg
from objects.injections import KwArg
class ObjectA(object):
@ -27,9 +27,9 @@ class Catalog(AbstractCatalog):
""":type: (objects.Config)"""
object_a = NewInstance(ObjectA,
InitArg('fee', config.FEE),
InitArg('price', config.PRICE),
InitArg('timezone', config.GLOBAL.TIMEZONE))
KwArg('fee', config.FEE),
KwArg('price', config.PRICE),
KwArg('timezone', config.GLOBAL.TIMEZONE))
""":type: (objects.Provider) -> ObjectA"""

View File

@ -1,11 +1,11 @@
"""Provider delegation example."""
from objects import AbstractCatalog
from objects.catalog import AbstractCatalog
from objects.providers import Singleton
from objects.providers import NewInstance
from objects.injections import InitArg
from objects.injections import KwArg
from objects.injections import Attribute
import sqlite3
@ -34,16 +34,16 @@ class Catalog(AbstractCatalog):
"""Catalog of objects providers."""
database = Singleton(sqlite3.Connection,
InitArg('database', ':memory:'),
KwArg('database', ':memory:'),
Attribute('row_factory', sqlite3.Row))
""":type: (objects.Provider) -> sqlite3.Connection"""
object_a = NewInstance(ObjectA,
InitArg('db', database))
KwArg('db', database))
""":type: (objects.Provider) -> ObjectA"""
object_b = Singleton(ObjectB,
InitArg('a_provider', object_a.delegate()))
KwArg('a_provider', object_a.delegate()))
""":type: (objects.Provider) -> ObjectB"""

View File

@ -1,12 +1,12 @@
"""External dependency example."""
from objects import AbstractCatalog
from objects.catalog import AbstractCatalog
from objects.providers import Singleton
from objects.providers import NewInstance
from objects.providers import ExternalDependency
from objects.injections import InitArg
from objects.injections import KwArg
from objects.injections import Attribute
import sqlite3
@ -39,18 +39,18 @@ class Catalog(AbstractCatalog):
""":type: (objects.Provider) -> sqlite3.Connection"""
object_a = NewInstance(ObjectA,
InitArg('db', database))
KwArg('db', database))
""":type: (objects.Provider) -> ObjectA"""
object_b = NewInstance(ObjectB,
InitArg('a', object_a),
InitArg('db', database))
KwArg('a', object_a),
KwArg('db', database))
""":type: (objects.Provider) -> ObjectB"""
# Satisfaction of external dependency.
Catalog.database.override(Singleton(sqlite3.Connection,
InitArg('database', ':memory:'),
KwArg('database', ':memory:'),
Attribute('row_factory', sqlite3.Row)))
# Catalog static provides.

View File

@ -1,12 +1,12 @@
"""Override example."""
from objects import AbstractCatalog
from objects import overrides
from objects.catalog import AbstractCatalog
from objects.catalog import override
from objects.providers import Singleton
from objects.providers import NewInstance
from objects.injections import InitArg
from objects.injections import KwArg
from objects.injections import Attribute
import sqlite3
@ -31,22 +31,22 @@ class Catalog(AbstractCatalog):
"""Catalog of objects providers."""
database = Singleton(sqlite3.Connection,
InitArg('database', ':memory:'),
KwArg('database', ':memory:'),
Attribute('row_factory', sqlite3.Row))
""":type: (objects.Provider) -> sqlite3.Connection"""
object_a = NewInstance(ObjectA,
InitArg('db', database))
KwArg('db', database))
""":type: (objects.Provider) -> ObjectA"""
@overrides(Catalog)
@override(Catalog)
class SandboxCatalog(Catalog):
"""Sandbox objects catalog with some mocks that overrides Catalog."""
object_a = NewInstance(ObjectAMock,
InitArg('db', Catalog.database))
KwArg('db', Catalog.database))
""":type: (objects.Provider) -> ObjectA"""

View File

@ -1,11 +1,11 @@
"""Concept example of objects catalogs."""
from objects import AbstractCatalog
from objects.catalog import AbstractCatalog
from objects.providers import Singleton
from objects.providers import NewInstance
from objects.injections import InitArg
from objects.injections import KwArg
from objects.injections import Attribute
from objects.errors import Error
@ -37,17 +37,17 @@ class Catalog(AbstractCatalog):
"""Catalog of objects providers."""
database = Singleton(sqlite3.Connection,
InitArg('database', ':memory:'),
KwArg('database', ':memory:'),
Attribute('row_factory', sqlite3.Row))
""":type: (objects.Provider) -> sqlite3.Connection"""
object_a = NewInstance(ObjectA,
InitArg('db', database))
KwArg('db', database))
""":type: (objects.Provider) -> ObjectA"""
object_b = NewInstance(ObjectB,
InitArg('a', object_a),
InitArg('db', database))
KwArg('a', object_a),
KwArg('db', database))
""":type: (objects.Provider) -> ObjectB"""

View File

@ -1,6 +1,7 @@
"""Objects."""
from .catalog import AbstractCatalog, overrides
from .catalog import AbstractCatalog
from .catalog import override
from .providers import Provider
from .providers import Delegate
@ -14,7 +15,7 @@ from .providers import Value
from .providers import Callable
from .providers import Config
from .injections import InitArg
from .injections import KwArg
from .injections import Attribute
from .injections import Method
@ -22,7 +23,7 @@ from .errors import Error
__all__ = ('AbstractCatalog',
'overrides',
'override',
# Providers
'Provider',
@ -38,7 +39,7 @@ __all__ = ('AbstractCatalog',
'Config',
# Injections
'InitArg',
'KwArg',
'Attribute',
'Method',

View File

@ -49,7 +49,7 @@ class AbstractCatalog(object):
overridden_provider.override(provider)
def overrides(catalog):
def override(catalog):
"""Catalog overriding decorator."""
def decorator(overriding_catalog):
"""Overriding decorator."""

View File

@ -1,6 +1,7 @@
"""Injections module."""
from .utils import is_provider
from .utils import ensure_is_injection
class Injection(object):
@ -23,12 +24,12 @@ class Injection(object):
return self.injectable
class InitArg(Injection):
class KwArg(Injection):
"""Init argument injection."""
"""Keyword argument injection."""
__IS_OBJECTS_INIT_ARG_INJECTION__ = True
__slots__ = ('__IS_OBJECTS_INIT_ARG_INJECTION__',)
__IS_OBJECTS_KWARG_INJECTION__ = True
__slots__ = ('__IS_OBJECTS_KWARG_INJECTION__',)
class Attribute(Injection):
@ -45,3 +46,22 @@ class Method(Injection):
__IS_OBJECTS_METHOD_INJECTION__ = True
__slots__ = ('__IS_OBJECTS_METHOD_INJECTION__',)
def inject(injection):
"""Inject decorator.
:type injection: Injection
:return: (callable) -> (callable)
"""
injection = ensure_is_injection(injection)
def decorator(callback):
"""Decorator."""
def decorated(*args, **kwargs):
"""Decorated."""
if injection.name not in kwargs:
kwargs[injection.name] = injection.value
return callback(*args, **kwargs)
return decorated
return decorator

View File

@ -3,8 +3,7 @@
from inspect import isclass
from .utils import ensure_is_provider
from .utils import is_injection
from .utils import is_init_arg_injection
from .utils import is_kwarg_injection
from .utils import is_attribute_injection
from .utils import is_method_injection
@ -77,7 +76,7 @@ class NewInstance(Provider):
New instance providers will create and return new instance on every call.
"""
__slots__ = ('provides', 'init_args', 'attributes', 'methods')
__slots__ = ('provides', 'kwargs', 'attributes', 'methods')
def __init__(self, provides, *injections):
"""Initializer."""
@ -85,9 +84,9 @@ class NewInstance(Provider):
raise Error('NewInstance provider expects to get class, ' +
'got {0} instead'.format(str(provides)))
self.provides = provides
self.init_args = tuple((injection
self.kwargs = tuple((injection
for injection in injections
if is_init_arg_injection(injection)))
if is_kwarg_injection(injection)))
self.attributes = tuple((injection
for injection in injections
if is_attribute_injection(injection)))
@ -102,7 +101,7 @@ class NewInstance(Provider):
return self.last_overriding(*args, **kwargs)
init_kwargs = dict(((injection.name, injection.value)
for injection in self.init_args))
for injection in self.kwargs))
init_kwargs.update(kwargs)
instance = self.provides(*args, **init_kwargs)
@ -228,7 +227,7 @@ class Callable(Provider):
self.callback = callback
self.injections = tuple((injection
for injection in injections
if is_injection(injection)))
if is_kwarg_injection(injection)))
super(Callable, self).__init__()
def __call__(self, *args, **kwargs):

View File

@ -25,10 +25,18 @@ def is_injection(instance):
hasattr(instance, '__IS_OBJECTS_INJECTION__'))
def is_init_arg_injection(instance):
"""Check if instance is init arg injection instance."""
def ensure_is_injection(instance):
"""Check if instance is injection instance, otherwise raise and error."""
if not is_injection(instance):
raise Error('Expected injection instance, '
'got {0}'.format(str(instance)))
return instance
def is_kwarg_injection(instance):
"""Check if instance is keyword argument injection instance."""
return (not isinstance(instance, class_types) and
hasattr(instance, '__IS_OBJECTS_INIT_ARG_INJECTION__'))
hasattr(instance, '__IS_OBJECTS_KWARG_INJECTION__'))
def is_attribute_injection(instance):

View File

@ -3,7 +3,7 @@
import unittest2 as unittest
from objects.catalog import AbstractCatalog
from objects.catalog import overrides
from objects.catalog import override
from objects.providers import Object
from objects.providers import Value
@ -53,7 +53,7 @@ class CatalogTests(unittest.TestCase):
def test_overriding(self):
"""Test catalog overriding with another catalog."""
@overrides(self.Catalog)
@override(self.Catalog)
class OverridingCatalog(self.Catalog):
"""Overriding catalog."""

View File

@ -3,7 +3,7 @@
import unittest2 as unittest
from objects.injections import Injection
from objects.injections import InitArg
from objects.injections import KwArg
from objects.injections import Attribute
from objects.injections import Method
@ -36,8 +36,8 @@ class InitArgTests(unittest.TestCase):
"""Init arg injection test cases."""
def test_init(self):
"""Test InitArg creation and initialization."""
injection = InitArg('some_arg_name', 'some_value')
"""Test KwArg creation and initialization."""
injection = KwArg('some_arg_name', 'some_value')
self.assertEqual(injection.name, 'some_arg_name')
self.assertEqual(injection.injectable, 'some_value')

View File

@ -14,8 +14,7 @@ from objects.providers import Value
from objects.providers import Callable
from objects.providers import Config
from objects.injections import Injection
from objects.injections import InitArg
from objects.injections import KwArg
from objects.injections import Attribute
from objects.injections import Method
@ -184,8 +183,8 @@ class NewInstanceTests(unittest.TestCase):
def test_call_with_init_args(self):
"""Test creation of new instances with init args injections."""
provider = NewInstance(self.Example,
InitArg('init_arg1', 'i1'),
InitArg('init_arg2', 'i2'))
KwArg('init_arg1', 'i1'),
KwArg('init_arg2', 'i2'))
instance1 = provider()
instance2 = provider()
@ -249,7 +248,7 @@ class NewInstanceTests(unittest.TestCase):
def test_call_with_context_kwargs(self):
"""Test creation of new instances with context kwargs."""
provider = NewInstance(self.Example,
InitArg('init_arg1', 1))
KwArg('init_arg1', 1))
instance1 = provider(init_arg2=22)
self.assertEqual(instance1.init_arg1, 1)
@ -399,9 +398,9 @@ class CallableTests(unittest.TestCase):
def setUp(self):
"""Set test cases environment up."""
self.provider = Callable(self.example,
Injection('arg1', 'a1'),
Injection('arg2', 'a2'),
Injection('arg3', 'a3'))
KwArg('arg1', 'a1'),
KwArg('arg2', 'a2'),
KwArg('arg3', 'a3'))
def test_init_with_not_callable(self):
"""Test creation of provider with not callable."""
@ -418,7 +417,7 @@ class CallableTests(unittest.TestCase):
def test_call_with_args(self):
"""Test provider call with kwargs priority."""
provider = Callable(self.example,
Injection('arg3', 'a3'))
KwArg('arg3', 'a3'))
self.assertEqual(provider(1, 2), (1, 2, 'a3'))
def test_call_with_kwargs_priority(self):

View File

@ -5,14 +5,15 @@ import unittest2 as unittest
from objects.utils import is_provider
from objects.utils import ensure_is_provider
from objects.utils import is_injection
from objects.utils import is_init_arg_injection
from objects.utils import ensure_is_injection
from objects.utils import is_kwarg_injection
from objects.utils import is_attribute_injection
from objects.utils import is_method_injection
from objects.providers import Provider
from objects.injections import Injection
from objects.injections import InitArg
from objects.injections import KwArg
from objects.injections import Attribute
from objects.injections import Method
@ -72,7 +73,7 @@ class IsInjectionTests(unittest.TestCase):
def test_with_subclass_instances(self):
"""Test with subclass instances."""
self.assertTrue(is_injection(InitArg('name', 'value')))
self.assertTrue(is_injection(KwArg('name', 'value')))
self.assertTrue(is_injection(Attribute('name', 'value')))
self.assertTrue(is_injection(Method('name', 'value')))
@ -89,29 +90,51 @@ class IsInjectionTests(unittest.TestCase):
self.assertFalse(is_injection(object()))
class IsInitArgInjectionTests(unittest.TestCase):
class EnsureIsInjectionTests(unittest.TestCase):
"""`is_init_arg_injection()` test cases."""
"""`ensure_is_injection` test cases."""
def test_with_instance(self):
"""Test with instance."""
self.assertTrue(is_init_arg_injection(InitArg('name', 'value')))
injection = Injection('name', 'value')
self.assertIs(ensure_is_injection(injection), injection)
def test_with_class(self):
"""Test with class."""
self.assertFalse(is_init_arg_injection(InitArg))
def test_with_parent_class(self):
"""Test with parent class."""
self.assertFalse(is_init_arg_injection(Injection))
self.assertRaises(Error, ensure_is_injection, Injection)
def test_with_string(self):
"""Test with string."""
self.assertFalse(is_init_arg_injection('some_string'))
self.assertRaises(Error, ensure_is_injection, 'some_string')
def test_with_object(self):
"""Test with object."""
self.assertFalse(is_init_arg_injection(object()))
self.assertRaises(Error, ensure_is_injection, object())
class IsKwArgInjectionTests(unittest.TestCase):
"""`is_kwarg_injection()` test cases."""
def test_with_instance(self):
"""Test with instance."""
self.assertTrue(is_kwarg_injection(KwArg('name', 'value')))
def test_with_class(self):
"""Test with class."""
self.assertFalse(is_kwarg_injection(KwArg))
def test_with_parent_class(self):
"""Test with parent class."""
self.assertFalse(is_kwarg_injection(Injection))
def test_with_string(self):
"""Test with string."""
self.assertFalse(is_kwarg_injection('some_string'))
def test_with_object(self):
"""Test with object."""
self.assertFalse(is_kwarg_injection(object()))
class IsAttributeInjectionTests(unittest.TestCase):