/
core.py
71 lines (50 loc) · 2.12 KB
/
core.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
"""Core functionality"""
from contextlib import contextmanager
from contextvars import ContextVar
from typing import Any, Callable, Generator, Optional, Dict
class DependencyError(Exception):
"""Base injectool error"""
Dependency = Any
Resolver = Callable[[], Any]
class Container:
"""Container for dependencies"""
def __init__(self, resolvers: Optional[Dict[Dependency, Resolver]] = None):
self._resolvers: Dict[Dependency, Resolver] = {} if resolvers is None else resolvers
self.set(Container, lambda: self)
def set(self, dependency: Dependency, resolve: Resolver):
"""Sets resolver for dependency"""
self._resolvers[dependency] = resolve
def resolve(self, dependency: Dependency) -> Any:
"""Resolve dependency"""
resolve = self._resolvers.get(dependency)
if resolve is None:
dependency_name = dependency.__name__ if hasattr(dependency, '__name__') else str(dependency)
raise DependencyError(f'Dependency "{dependency_name}" is not found')
return resolve()
def copy(self) -> 'Container':
"""returns new container with same dependencies"""
return Container(self._resolvers.copy())
_DEFAULT_CONTAINER = Container()
def set_default_container(container: Container):
"""Sets default container"""
global _DEFAULT_CONTAINER
_DEFAULT_CONTAINER = container
_CURRENT_CONTAINER = ContextVar('dependency_container')
def get_container() -> Container:
"""Returns current container"""
return _CURRENT_CONTAINER.get(_DEFAULT_CONTAINER)
@contextmanager
def use_container(container: Optional[Container] = None) -> Generator[Container, None, None]:
"""
Uses passed container for registering and resolving dependencies
Creates new if container doesn't exist.
"""
container = container if container else Container()
reset_token = _CURRENT_CONTAINER.set(container)
try:
yield container
finally:
_CURRENT_CONTAINER.reset(reset_token)
def resolve(dependency: Dependency):
"""resolves dependency for current container"""
return get_container().resolve(dependency)