Make some refactoring and add tests

This commit is contained in:
Roman Mogylatov 2021-03-02 17:04:50 -05:00
parent a948ff37b7
commit 8eb7501ec1
8 changed files with 2949 additions and 2728 deletions

View File

@ -3606,7 +3606,7 @@ static PyObject *__pyx_pf_19dependency_injector_10containers_20DeclarativeContai
static PyObject *__pyx_pf_19dependency_injector_10containers_20DeclarativeContainer_4reset_last_overriding(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_cls); /* proto */
static PyObject *__pyx_pf_19dependency_injector_10containers_20DeclarativeContainer_6reset_override(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_cls); /* proto */
static PyObject *__pyx_pf_19dependency_injector_10containers_21SingletonResetContext___init__(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_self, PyObject *__pyx_v_container); /* proto */
static PyObject *__pyx_pf_19dependency_injector_10containers_21SingletonResetContext_2__enter__(CYTHON_UNUSED PyObject *__pyx_self, CYTHON_UNUSED PyObject *__pyx_v_self); /* proto */
static PyObject *__pyx_pf_19dependency_injector_10containers_21SingletonResetContext_2__enter__(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_self); /* proto */
static PyObject *__pyx_pf_19dependency_injector_10containers_21SingletonResetContext_4__exit__(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_self, CYTHON_UNUSED PyObject *__pyx_v__); /* proto */
static PyObject *__pyx_pf_19dependency_injector_10containers_8override__decorator(PyObject *__pyx_self, PyObject *__pyx_v_overriding_container); /* proto */
static PyObject *__pyx_pf_19dependency_injector_10containers_4override(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_container); /* proto */
@ -14649,7 +14649,7 @@ static PyObject *__pyx_pf_19dependency_injector_10containers_21SingletonResetCon
* self._container = container
*
* def __enter__(self): # <<<<<<<<<<<<<<
* ...
* return self._container
*
*/
@ -14667,20 +14667,50 @@ static PyObject *__pyx_pw_19dependency_injector_10containers_21SingletonResetCon
return __pyx_r;
}
static PyObject *__pyx_pf_19dependency_injector_10containers_21SingletonResetContext_2__enter__(CYTHON_UNUSED PyObject *__pyx_self, CYTHON_UNUSED PyObject *__pyx_v_self) {
static PyObject *__pyx_pf_19dependency_injector_10containers_21SingletonResetContext_2__enter__(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_self) {
PyObject *__pyx_r = NULL;
__Pyx_RefNannyDeclarations
PyObject *__pyx_t_1 = NULL;
int __pyx_lineno = 0;
const char *__pyx_filename = NULL;
int __pyx_clineno = 0;
__Pyx_RefNannySetupContext("__enter__", 0);
/* "dependency_injector/containers.pyx":649
*
* def __enter__(self):
* return self._container # <<<<<<<<<<<<<<
*
* def __exit__(self, *_):
*/
__Pyx_XDECREF(__pyx_r);
__pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_container_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 649, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_1);
__pyx_r = __pyx_t_1;
__pyx_t_1 = 0;
goto __pyx_L0;
/* "dependency_injector/containers.pyx":648
* self._container = container
*
* def __enter__(self): # <<<<<<<<<<<<<<
* return self._container
*
*/
/* function exit code */
__pyx_r = Py_None; __Pyx_INCREF(Py_None);
__pyx_L1_error:;
__Pyx_XDECREF(__pyx_t_1);
__Pyx_AddTraceback("dependency_injector.containers.SingletonResetContext.__enter__", __pyx_clineno, __pyx_lineno, __pyx_filename);
__pyx_r = NULL;
__pyx_L0:;
__Pyx_XGIVEREF(__pyx_r);
__Pyx_RefNannyFinishContext();
return __pyx_r;
}
/* "dependency_injector/containers.pyx":651
* ...
* return self._container
*
* def __exit__(self, *_): # <<<<<<<<<<<<<<
* self._container.reset_singletons()
@ -14796,7 +14826,7 @@ static PyObject *__pyx_pf_19dependency_injector_10containers_21SingletonResetCon
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
/* "dependency_injector/containers.pyx":651
* ...
* return self._container
*
* def __exit__(self, *_): # <<<<<<<<<<<<<<
* self._container.reset_singletons()
@ -23499,7 +23529,7 @@ static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) {
* self._container = container
*
* def __enter__(self): # <<<<<<<<<<<<<<
* ...
* return self._container
*
*/
__pyx_tuple__101 = PyTuple_Pack(1, __pyx_n_s_self_2); if (unlikely(!__pyx_tuple__101)) __PYX_ERR(0, 648, __pyx_L1_error)
@ -23508,7 +23538,7 @@ static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) {
__pyx_codeobj__102 = (PyObject*)__Pyx_PyCode_New(1, 0, 1, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__101, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_src_dependency_injector_containe, __pyx_n_s_enter, 648, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__102)) __PYX_ERR(0, 648, __pyx_L1_error)
/* "dependency_injector/containers.pyx":651
* ...
* return self._container
*
* def __exit__(self, *_): # <<<<<<<<<<<<<<
* self._container.reset_singletons()
@ -25115,7 +25145,7 @@ if (!__Pyx_RefNanny) {
* self._container = container
*
* def __enter__(self): # <<<<<<<<<<<<<<
* ...
* return self._container
*
*/
__pyx_t_7 = __Pyx_CyFunction_New(&__pyx_mdef_19dependency_injector_10containers_21SingletonResetContext_3__enter__, 0, __pyx_n_s_SingletonResetContext___enter, NULL, __pyx_n_s_dependency_injector_containers, __pyx_d, ((PyObject *)__pyx_codeobj__102)); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 648, __pyx_L1_error)
@ -25124,7 +25154,7 @@ if (!__Pyx_RefNanny) {
__Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
/* "dependency_injector/containers.pyx":651
* ...
* return self._container
*
* def __exit__(self, *_): # <<<<<<<<<<<<<<
* self._container.reset_singletons()

View File

@ -1,4 +1,5 @@
from typing import (
Generic,
Type,
Dict,
Tuple,
@ -20,6 +21,7 @@ from .providers import Provider, Self, ProviderParent
C_Base = TypeVar('C_Base', bound='Container')
C = TypeVar('C', bound='DeclarativeContainer')
C_Overriding = TypeVar('C_Overriding', bound='DeclarativeContainer')
T = TypeVar('T')
TT = TypeVar('TT')
@ -44,7 +46,7 @@ class Container:
def init_resources(self) -> Optional[Awaitable]: ...
def shutdown_resources(self) -> Optional[Awaitable]: ...
def apply_container_providers_overridings(self) -> None: ...
def reset_singletons(self) -> SingletonResettingContext: ...
def reset_singletons(self) -> SingletonResetContext[C_Base]: ...
def check_dependencies(self) -> None: ...
@overload
def resolve_provider_name(self, provider: Provider) -> str: ...
@ -72,9 +74,9 @@ class DeclarativeContainer(Container):
def __init__(self, **overriding_providers: Union[Provider, Any]) -> None: ...
class SingletonResettingContext:
def __init__(self, container: Container): ...
def __enter__(self) -> None: ...
class SingletonResetContext(Generic[T]):
def __init__(self, container: T): ...
def __enter__(self) -> T: ...
def __exit__(self, *_: Any) -> None: ...

View File

@ -646,7 +646,7 @@ class SingletonResetContext:
self._container = container
def __enter__(self):
...
return self._container
def __exit__(self, *_):
self._container.reset_singletons()

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,8 @@ Injection = Any
ProviderParent = Union['Provider', Any]
T = TypeVar('T')
TT = TypeVar('TT')
P = TypeVar('P', bound='Provider')
BS = TypeVar('BS', bound='BaseSingleton')
class Provider(Generic[T]):
@ -56,7 +58,7 @@ class Provider(Generic[T]):
def overridden(self) -> Tuple[Provider]: ...
@property
def last_overriding(self) -> Optional[Provider]: ...
def override(self, provider: Union[Provider, Any]) -> OverridingContext: ...
def override(self, provider: Union[Provider, Any]) -> OverridingContext[P]: ...
def reset_last_overriding(self) -> None: ...
def reset_override(self) -> None: ...
def delegate(self) -> Provider: ...
@ -103,7 +105,7 @@ class Dependency(Provider[T]):
def default(self) -> Provider[T]: ...
@property
def is_defined(self) -> bool: ...
def provided_by(self, provider: Provider) -> OverridingContext: ...
def provided_by(self, provider: Provider) -> OverridingContext[P]: ...
@property
def parent(self) -> Optional[ProviderParent]: ...
@property
@ -147,7 +149,7 @@ class DelegatedCallable(Callable[T]): ...
class AbstractCallable(Callable[T]):
def override(self, provider: Callable) -> OverridingContext: ...
def override(self, provider: Callable) -> OverridingContext[P]: ...
class CallableDelegate(Delegate):
@ -161,7 +163,7 @@ class DelegatedCoroutine(Coroutine[T]): ...
class AbstractCoroutine(Coroutine[T]):
def override(self, provider: Coroutine) -> OverridingContext: ...
def override(self, provider: Coroutine) -> OverridingContext[P]: ...
class CoroutineDelegate(Delegate):
@ -206,7 +208,7 @@ class Configuration(Object[Any]):
def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
def get_name(self) -> str: ...
def get(self, selector: str) -> Any: ...
def set(self, selector: str, value: Any) -> OverridingContext: ...
def set(self, selector: str, value: Any) -> OverridingContext[P]: ...
def reset_cache(self) -> None: ...
def update(self, value: Any) -> None: ...
def from_ini(self, filepath: Union[Path, str], required: bool = False) -> None: ...
@ -244,7 +246,7 @@ class DelegatedFactory(Factory[T]): ...
class AbstractFactory(Factory[T]):
def override(self, provider: Factory) -> OverridingContext: ...
def override(self, provider: Factory) -> OverridingContext[P]: ...
class FactoryDelegate(Delegate):
@ -287,8 +289,8 @@ class BaseSingleton(Provider[T]):
def add_attributes(self, **kwargs: Injection) -> BaseSingleton[T]: ...
def set_attributes(self, **kwargs: Injection) -> BaseSingleton[T]: ...
def clear_attributes(self) -> BaseSingleton[T]: ...
def reset(self) -> SingletonResetContext: ...
def full_reset(self) -> SingletonFullResetContext: ...
def reset(self) -> SingletonResetContext[BS]: ...
def full_reset(self) -> SingletonFullResetContext[BS]: ...
class Singleton(BaseSingleton[T]): ...
@ -310,7 +312,7 @@ class DelegatedThreadLocalSingleton(ThreadLocalSingleton[T]): ...
class AbstractSingleton(BaseSingleton[T]):
def override(self, provider: BaseSingleton) -> OverridingContext: ...
def override(self, provider: BaseSingleton) -> OverridingContext[P]: ...
class SingletonDelegate(Delegate):
@ -408,15 +410,15 @@ class MethodCaller(Provider, ProvidedInstanceFluentInterface):
def __init__(self, provider: Provider, *args: Injection, **kwargs: Injection) -> None: ...
class OverridingContext:
class OverridingContext(Generic[T]):
def __init__(self, overridden: Provider, overriding: Provider): ...
def __enter__(self) -> Provider: ...
def __enter__(self) -> T: ...
def __exit__(self, *_: Any) -> None: ...
class BaseSingletonResetContext:
def __init__(self, provider: Provider): ...
def __enter__(self) -> None: ...
class BaseSingletonResetContext(Generic[T]):
def __init__(self, provider: T): ...
def __enter__(self) -> T: ...
def __exit__(self, *_: Any) -> None: ...

View File

@ -2743,7 +2743,6 @@ cdef class ThreadSafeSingleton(BaseSingleton):
self.__storage = None
return SingletonResetContext(self)
cpdef object _provide(self, tuple args, dict kwargs):
"""Return single instance."""
instance = self.__storage
@ -2820,9 +2819,16 @@ cdef class ThreadLocalSingleton(BaseSingleton):
:rtype: None
"""
if __is_future_or_coroutine(self.__storage.instance):
asyncio.ensure_future(self.__storage.instance).cancel()
try:
instance = self.__storage.instance
except AttributeError:
return SingletonResetContext(self)
if __is_future_or_coroutine(instance):
asyncio.ensure_future(instance).cancel()
del self.__storage.instance
return SingletonResetContext(self)
cpdef object _provide(self, tuple args, dict kwargs):

View File

@ -336,6 +336,36 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
self.assertIs(obj32, obj42)
self.assertIs(obj33, obj43)
def test_reset_singletons_context_manager(self):
class Item:
def __init__(self, dependency):
self.dependency = dependency
class Container(containers.DeclarativeContainer):
dependent = providers.Singleton(object)
singleton = providers.Singleton(Item, dependency=dependent)
container = Container()
instance1 = container.singleton()
with container.reset_singletons():
instance2 = container.singleton()
instance3 = container.singleton()
self.assertEqual(len({instance1, instance2, instance3}), 3)
self.assertEqual(
len({instance1.dependency, instance2.dependency, instance3.dependency}),
3,
)
def test_reset_singletons_context_manager_as_attribute(self):
container = containers.DeclarativeContainer()
with container.reset_singletons() as alias:
pass
self.assertIs(container, alias)
def test_check_dependencies(self):
class SubContainer(containers.DeclarativeContainer):
dependency = providers.Dependency()

View File

@ -370,6 +370,23 @@ class _BaseSingletonTestCase(object):
self.assertIsNot(instance1, instance2)
def test_reset_context_manager(self):
singleton = self.singleton_cls(object)
instance1 = singleton()
with singleton.reset():
instance2 = singleton()
instance3 = singleton()
self.assertEqual(len({instance1, instance2, instance3}), 3)
def test_reset_context_manager_as_attribute(self):
singleton = self.singleton_cls(object)
with singleton.reset() as alias:
pass
self.assertIs(singleton, alias)
def test_full_reset(self):
dependent_singleton = providers.Singleton(object)
provider = self.singleton_cls(dict, dependency=dependent_singleton)
@ -386,6 +403,33 @@ class _BaseSingletonTestCase(object):
self.assertIsNot(dependent_instance1, dependent_instance2)
self.assertIsNot(instance1, instance2)
def test_full_reset_context_manager(self):
class Item:
def __init__(self, dependency):
self.dependency = dependency
dependent_singleton = providers.Singleton(object)
singleton = self.singleton_cls(Item, dependency=dependent_singleton)
instance1 = singleton()
with singleton.full_reset():
instance2 = singleton()
instance3 = singleton()
self.assertEqual(len({instance1, instance2, instance3}), 3)
self.assertEqual(
len({instance1.dependency, instance2.dependency, instance3.dependency}),
3,
)
def test_full_reset_context_manager_as_attribute(self):
singleton = self.singleton_cls(object)
with singleton.full_reset() as alias:
pass
self.assertIs(singleton, alias)
class SingletonTests(_BaseSingletonTestCase, unittest.TestCase):
@ -445,6 +489,16 @@ class ThreadLocalSingletonTests(_BaseSingletonTestCase, unittest.TestCase):
self.assertIsNot(instance1, instance2)
def test_reset_clean(self):
provider = providers.ThreadLocalSingleton(Example)
instance1 = provider()
provider.reset()
provider.reset()
instance2 = provider()
self.assertIsNot(instance1, instance2)
class DelegatedThreadLocalSingletonTests(_BaseSingletonTestCase,
unittest.TestCase):