From d85d58b747b27b3dd851542b62d088531a006a1c Mon Sep 17 00:00:00 2001 From: Krystian Kichewko Date: Sat, 15 Jul 2017 15:02:35 +0200 Subject: [PATCH 1/3] Add default scope support --- .gitignore | 2 ++ CHANGES | 5 +++++ injector.py | 19 ++++++++++++++----- injector_test.py | 11 +++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) 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..4ccb544 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,8 @@ 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: + :class:`NoScope`. If you use Python 3 you can make Injector use constructor parameter annotations to determine class dependencies. The following code:: @@ -643,9 +648,12 @@ 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 = () @@ -653,7 +661,8 @@ def __init__(self, modules=None, auto_bind=True, parent=None): self.parent = parent # 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..2d1e799 100644 --- a/injector_test.py +++ b/injector_test.py @@ -1249,3 +1249,14 @@ 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=singleton) + assert i2.get(A) is i2.get(A) From afc9a6de91424e4e4d478a073e8299bbeb529822 Mon Sep 17 00:00:00 2001 From: Krystian Kichewko Date: Mon, 17 Jul 2017 21:47:39 +0200 Subject: [PATCH 2/3] Add default scope support --- injector.py | 10 ++++++++-- injector_test.py | 21 ++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/injector.py b/injector.py index 4ccb544..d0a6352 100644 --- a/injector.py +++ b/injector.py @@ -626,8 +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: - :class:`NoScope`. + :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:: @@ -660,6 +662,10 @@ def __init__(self, modules=None, auto_bind=True, parent=None, scope=None): 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, scope=scope) diff --git a/injector_test.py b/injector_test.py index 2d1e799..e84a192 100644 --- a/injector_test.py +++ b/injector_test.py @@ -10,12 +10,11 @@ """Functional tests for the "Injector" dependency injection framework.""" -from contextlib import contextmanager -from typing import Any import abc import threading -import traceback import warnings +from contextlib import contextmanager +from typing import Any import pytest @@ -25,7 +24,7 @@ CircularDependency, Module, Key, SingletonScope, ScopeDecorator, with_injector, AssistedBuilder, BindingKey, SequenceKey, MappingKey, provider, ProviderOf, ClassAssistedBuilder, - ) + NoScope) def prepare_basic_injection(): @@ -1258,5 +1257,17 @@ class A: i1 = Injector() assert i1.get(A) is not i1.get(A) - i2 = Injector(scope=singleton) + 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 From b9b07e194cbe55b5735a7f51a2d96b56f118d1d9 Mon Sep 17 00:00:00 2001 From: Krystian Kichewko Date: Mon, 17 Jul 2017 21:48:49 +0200 Subject: [PATCH 3/3] Add default scope support --- injector_test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/injector_test.py b/injector_test.py index e84a192..a00ab97 100644 --- a/injector_test.py +++ b/injector_test.py @@ -10,11 +10,12 @@ """Functional tests for the "Injector" dependency injection framework.""" +from contextlib import contextmanager +from typing import Any import abc import threading +import traceback import warnings -from contextlib import contextmanager -from typing import Any import pytest