Merge branch 'master' into child-injectors

Conflicts:
	injector_test.py
This commit is contained in:
Jakub Stasiak 2013-02-18 17:41:41 +00:00
commit 3de2f605fc
3 changed files with 65 additions and 11 deletions

View File

@ -272,7 +272,8 @@ Assisted injection
Sometimes there are classes that have injectable and non-injectable parameters in their
constructors. Let's have for example::
>>> Database = Key('Database')
>>> class Database(object): pass
>>> class User(object):
... def __init__(self, name):
... self.name = name

View File

@ -104,7 +104,6 @@ class ClassProvider(Provider):
def get(self):
return self.injector.create_object(self._cls)
class CallableProvider(Provider):
"""Provides something using a callable."""
@ -272,8 +271,8 @@ class Binder(object):
elif issubclass(type(to), type):
return ClassProvider(to, self.injector)
elif isinstance(interface, AssistedBuilder):
self.injector.install_into(interface)
return InstanceProvider(interface)
builder = AssistedBuilderImplementation(interface.interface, self.injector)
return InstanceProvider(builder)
elif isinstance(to, interface):
return InstanceProvider(to)
elif issubclass(type(interface), type):
@ -510,7 +509,11 @@ class Injector(object):
try:
instance.__init__(**additional_kwargs)
except TypeError as e:
raise CallError(instance, instance.__init__.__func__, (), additional_kwargs, e)
# The reason why getattr() fallback is used here is that
# __init__.__func__ apparently doesn't exist for Key-type objects
raise CallError(instance,
getattr(instance.__init__, '__func__', instance.__init__),
(), additional_kwargs, e)
return instance
def install_into(self, instance):
@ -706,6 +709,10 @@ def Annotation(name):
class BaseKey(object):
"""Base type for binding keys."""
def __init__(self):
raise Exception('Instantiation of %s prohibited - it is derived from BaseKey '
'so most likely you should bind it to something.' % (self.__class__,))
def Key(name):
"""Create a new type key.
@ -734,13 +741,30 @@ def Key(name):
pass
return type(name, (BaseKey,), {})
class AssistedBuilder(object):
def __init__(self, cls):
self.cls = cls
class AssistedBuilder(tuple):
def __new__(cls, interface):
return super(AssistedBuilder, cls).__new__(cls, (interface,))
@property
def interface(self):
return self[0]
class AssistedBuilderImplementation(object):
def __init__(self, interface, injector):
self.interface = interface
self.injector = injector
def build(self, **kwargs):
return self.__injector__.create_object(self.cls, additional_kwargs=kwargs)
key = BindingKey(self.interface, None)
binder = self.injector.binder
binding = binder.get_binding(None, key)
provider = binding.provider
try:
cls = provider._cls
except AttributeError:
raise Error('Assisted building works only with ClassProviders, '
'got %r for %r' % (provider, self.interface))
return self.injector.create_object(cls, additional_kwargs=kwargs)
def _describe(c):
if hasattr(c, '__name__'):

View File

@ -20,7 +20,7 @@ import pytest
from injector import (Binder, CallError, Injector, Scope, InstanceProvider, ClassProvider,
inject, singleton, threadlocal, UnsatisfiedRequirement,
CircularDependency, Module, provides, Key, extends, SingletonScope,
ScopeDecorator, with_injector, AssistedBuilder)
ScopeDecorator, with_injector, AssistedBuilder, BindingKey)
def prepare_basic_injection():
@ -59,6 +59,10 @@ def test_scopes_are_only_bound_to_root_injector():
parent.binder.bind(A, to=A, scope=singleton)
assert (parent.get(A) is child.get(A))
def test_key_cannot_be_instantiated():
with pytest.raises(Exception):
Interface = Key('Interface')
i = Interface()
def test_get_default_injected_instances():
A, B = prepare_basic_injection()
@ -690,6 +694,31 @@ def test_assisted_builder_works_when_injected():
x = injector.get(X)
assert ((x.obj.a, x.obj.b) == (str(), 234))
def test_assisted_builder_uses_bindings():
Interface = Key('Interface')
def configure(binder):
binder.bind(Interface, to=NeedsAssistance)
injector = Injector(configure)
builder = injector.get(AssistedBuilder(Interface))
x = builder.build(b=333)
assert ((type(x), x.b) == (NeedsAssistance, 333))
def test_assisted_builder_injection_is_safe_to_use_with_multiple_injectors():
class X(object):
@inject(builder=AssistedBuilder(NeedsAssistance))
def y(self, builder):
return builder
i1, i2 = Injector(), Injector()
b1 = i1.get(X).y()
b2 = i2.get(X).y()
assert ((b1.injector, b2.injector) == (i1, i2))
def test_assisted_builder_injection_uses_the_same_binding_key_every_time():
# if we have different BindingKey for every AssistedBuilder(...) we will get memory leak
gen_key = lambda: BindingKey(AssistedBuilder(NeedsAssistance), None)
assert gen_key() == gen_key()
class TestThreadSafety(object):
def setup(self):
def configure(binder):