diff --git a/.gitignore b/.gitignore index ba2cd8b..068915e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ docs/_build/ build/ htmlcov/ *,cover +.python-version +.idea diff --git a/CHANGES b/CHANGES index 71dce20..8f339b6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,11 @@ Injector Change Log =================== +0.14.0 +------ + +- Add default scope support + 0.13.1 ------ diff --git a/injector.py b/injector.py index 7cab2f6..d0a6352 100644 --- a/injector.py +++ b/injector.py @@ -30,7 +30,7 @@ __author__ = 'Alec Thomas ' -__version__ = '0.13.1' +__version__ = '0.14.0' __version_tag__ = '' log = logging.getLogger('injector') @@ -265,17 +265,20 @@ class Binder: """ @private - def __init__(self, injector, auto_bind=True, parent=None): + def __init__(self, injector, auto_bind=True, parent=None, scope=None): """Create a new Binder. :param injector: Injector we are binding for. :param auto_bind: Whether to automatically bind missing types. :param parent: Parent binder. + :param scope: Default scope used, when no scope is provided + in :meth:`Binder.bind` method. :class:`NoScope` is used by default. """ self.injector = injector self._auto_bind = auto_bind self._bindings = {} self.parent = parent + self.scope = scope or NoScope def bind(self, interface, to=None, scope=None): """Bind an interface to an implementation. @@ -383,7 +386,7 @@ def configure(self, binder): def create_binding(self, interface, to=None, scope=None): provider = self.provider_for(interface, to) - scope = scope or getattr(to or interface, '__scope__', NoScope) + scope = scope or getattr(to or interface, '__scope__', self.scope) if isinstance(scope, ScopeDecorator): scope = scope.scope return Binding(interface, provider, scope) @@ -623,6 +626,10 @@ class Injector: :param auto_bind: Whether to automatically bind missing types. :param parent: Parent injector. + :param scope: Scope of created objects, if no scope provided. + Default scope is :class:`NoScope`. + For child injectors parent's scope is used if no scope + provided. If you use Python 3 you can make Injector use constructor parameter annotations to determine class dependencies. The following code:: @@ -643,17 +650,25 @@ def __init__(self, a:A): .. versionchanged:: 0.13.0 ``use_annotations`` parameter is removed + + .. versionchanged:: 0.14.0 + ``scope`` parameter added """ - def __init__(self, modules=None, auto_bind=True, parent=None): + def __init__(self, modules=None, auto_bind=True, parent=None, scope=None): # Stack of keys currently being injected. Used to detect circular # dependencies. self._stack = () self.parent = parent + if not scope and parent: + scope = parent.scope + self.scope = scope + # Binder - self.binder = Binder(self, auto_bind=auto_bind, parent=parent and parent.binder) + self.binder = Binder(self, auto_bind=auto_bind, + parent=parent and parent.binder, scope=scope) if not modules: modules = [] diff --git a/injector_test.py b/injector_test.py index 53f8bcb..a00ab97 100644 --- a/injector_test.py +++ b/injector_test.py @@ -25,7 +25,7 @@ CircularDependency, Module, Key, SingletonScope, ScopeDecorator, with_injector, AssistedBuilder, BindingKey, SequenceKey, MappingKey, provider, ProviderOf, ClassAssistedBuilder, - ) + NoScope) def prepare_basic_injection(): @@ -1249,3 +1249,26 @@ def __init__(self, a: A, builder: ClassAssistedBuilder[B]): assert isinstance(c, C) assert isinstance(c.b, B) assert isinstance(c.b.a, A) + + +def test_default_scope_settings(): + class A: + pass + + i1 = Injector() + assert i1.get(A) is not i1.get(A) + + i2 = Injector(scope=SingletonScope) + assert i2.get(A) is i2.get(A) + + +def test_default_scope_parents(): + class A: + pass + + parent = Injector(scope=SingletonScope) + child1 = Injector(parent=parent) + child2 = Injector(parent=parent, scope=NoScope) + + assert child1.scope == SingletonScope + assert child2.scope == NoScope