-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #93 from botstory/feature/di
Feature/di
- Loading branch information
Showing
29 changed files
with
974 additions
and
140 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from . import desciption as desc_module, inject as inject_module, injector_service | ||
|
||
__all__ = [] | ||
|
||
injector = injector_service.Injector() | ||
|
||
bind = injector.bind | ||
child_scope = injector.child_scope | ||
clear_instances = injector.clear_instances | ||
desc = desc_module.desc | ||
get = injector.get | ||
inject = inject_module.inject | ||
|
||
__all__.extend([bind, child_scope, clear_instances, desc, inject]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import inspect | ||
from .parser import camel_case_to_underscore | ||
from .. import di | ||
|
||
|
||
def desc(t=None, reg=True): | ||
""" | ||
Describe Class Dependency | ||
:param reg: should we register this class as well | ||
:param t: custom type as well | ||
:return: | ||
""" | ||
|
||
def decorated_fn(cls): | ||
if not inspect.isclass(cls): | ||
return NotImplemented('For now we can only describe classes') | ||
name = t or camel_case_to_underscore(cls.__name__)[0] | ||
if reg: | ||
di.injector.register(name, cls) | ||
else: | ||
di.injector.describe(name, cls) | ||
return cls | ||
|
||
return decorated_fn |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import inspect | ||
|
||
from .parser import camel_case_to_underscore | ||
|
||
from .. import di | ||
|
||
|
||
def inject(t=None): | ||
def decorated_fn(fn): | ||
if inspect.isclass(fn): | ||
name = t or camel_case_to_underscore(fn.__name__)[0] | ||
print('register {} on name {}'.format(fn, name)) | ||
di.injector.register(name, fn) | ||
elif inspect.isfunction(fn): | ||
di.injector.requires(fn) | ||
else: | ||
# I'm not sure whether it possible case | ||
raise NotImplementedError('try decorate {}'.format(fn)) | ||
return fn | ||
|
||
return decorated_fn |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import pytest | ||
from .. import di | ||
|
||
|
||
def test_inject_decorator(): | ||
with di.child_scope(): | ||
@di.inject() | ||
class OneClass: | ||
def __init__(self): | ||
pass | ||
|
||
assert isinstance(di.injector.get('one_class'), OneClass) | ||
|
||
|
||
def test_bind_singleton_instance_by_default(): | ||
with di.child_scope(): | ||
@di.inject() | ||
class OneClass: | ||
def __init__(self): | ||
pass | ||
|
||
assert di.injector.get('one_class') == di.injector.get('one_class') | ||
|
||
|
||
def test_inject_into_method_of_class(): | ||
with di.child_scope(): | ||
@di.inject() | ||
class OuterClass: | ||
@di.inject() | ||
def inner(self, inner_class): | ||
self.inner_class = inner_class | ||
|
||
@di.inject() | ||
class InnerClass: | ||
pass | ||
|
||
outer = di.injector.get('outer_class') | ||
assert isinstance(outer, OuterClass) | ||
assert isinstance(outer.inner_class, InnerClass) | ||
|
||
|
||
def test_bind_should_inject_deps_in_decorated_methods_(): | ||
with di.child_scope(): | ||
@di.inject() | ||
class OuterClass: | ||
@di.inject() | ||
def inner(self, inner_class): | ||
self.inner_class = inner_class | ||
|
||
@di.inject() | ||
class InnerClass: | ||
pass | ||
|
||
outer = di.bind(OuterClass()) | ||
assert isinstance(outer, OuterClass) | ||
assert isinstance(outer.inner_class, InnerClass) | ||
|
||
|
||
def test_inject_default_value_if_we_dont_have_dep(): | ||
with di.child_scope(): | ||
@di.inject() | ||
class OuterClass: | ||
@di.inject() | ||
def inner(self, inner_class='Hello World!'): | ||
self.inner_class = inner_class | ||
|
||
outer = di.bind(OuterClass()) | ||
assert isinstance(outer, OuterClass) | ||
assert outer.inner_class == 'Hello World!' | ||
|
||
|
||
def test_no_autoupdate_deps_on_new_instance_comes(): | ||
with di.child_scope(): | ||
@di.inject() | ||
class OuterClass: | ||
@di.inject() | ||
def inner(self, inner_class=None): | ||
self.inner_class = inner_class | ||
|
||
outer = di.bind(OuterClass(), auto=False) | ||
|
||
@di.inject() | ||
class InnerClass: | ||
pass | ||
|
||
assert isinstance(outer, OuterClass) | ||
assert outer.inner_class is None | ||
|
||
|
||
def test_autoupdate_deps_on_new_instance_comes(): | ||
with di.child_scope(): | ||
@di.inject() | ||
class OuterClass: | ||
@di.inject() | ||
def inner(self, inner_class=None): | ||
self.inner_class = inner_class | ||
|
||
outer = di.bind(OuterClass(), auto=True) | ||
|
||
@di.inject() | ||
class InnerClass: | ||
pass | ||
|
||
assert isinstance(outer, OuterClass) | ||
assert isinstance(outer.inner_class, InnerClass) | ||
|
||
|
||
def test_fail_on_cyclic_deps(): | ||
with di.child_scope(): | ||
@di.inject() | ||
class FirstClass: | ||
@di.inject() | ||
def deps(self, second_class=None): | ||
self.second_class = second_class | ||
|
||
@di.inject() | ||
class SecondClass: | ||
@di.inject() | ||
def deps(self, first_class=None): | ||
self.first_class = first_class | ||
|
||
first_class = di.injector.get('first_class') | ||
assert isinstance(first_class.second_class, SecondClass) | ||
assert isinstance(first_class.second_class.first_class, FirstClass) | ||
|
||
|
||
def test_custom_type(): | ||
with di.child_scope(): | ||
@di.inject('qwerty') | ||
class OneClass: | ||
pass | ||
|
||
assert isinstance(di.injector.get('qwerty'), OneClass) | ||
|
||
|
||
def test_fail_on_incorrect_using(): | ||
with pytest.raises(NotImplementedError): | ||
di.inject()('qwerty') |
Oops, something went wrong.