|
1
|
1 |
|
import functools |
|
2
|
1 |
|
import inspect |
|
3
|
1 |
|
from typing import Dict, Type, Union, Any, TypeVar, Callable |
|
4
|
|
|
|
|
5
|
1 |
|
from .interfaces import SpecialDepDefinition |
|
6
|
1 |
|
from .exceptions import UnresolvableType |
|
7
|
1 |
|
from .definitions import normalise |
|
8
|
|
|
|
|
9
|
1 |
|
UNRESOLVABLE_TYPES = [str, int, float, bool] |
|
10
|
|
|
|
|
11
|
1 |
|
X = TypeVar("X") |
|
12
|
|
|
|
|
13
|
1 |
|
DepDefinition = Any |
|
14
|
|
|
|
|
15
|
|
|
|
|
16
|
1 |
|
class Container: |
|
17
|
1 |
|
_registered_types: Dict[Type, SpecialDepDefinition] = {} |
|
18
|
|
|
|
|
19
|
1 |
|
def define(self, dep: Union[Type[X], Type], resolver: DepDefinition) -> None: |
|
20
|
1 |
|
self._registered_types[dep] = normalise(resolver, self) |
|
21
|
|
|
|
|
22
|
1 |
|
def resolve(self, dep_type: Type[X], suppress_error=False) -> X: |
|
23
|
1 |
|
try: |
|
24
|
1 |
|
if dep_type in UNRESOLVABLE_TYPES: |
|
25
|
1 |
|
raise UnresolvableType(f"Cannot construct type {dep_type}") |
|
26
|
1 |
|
registered_type = self._registered_types.get(dep_type, dep_type) |
|
27
|
1 |
|
return self._build(registered_type) |
|
28
|
1 |
|
except UnresolvableType as inner_error: |
|
29
|
1 |
|
if not suppress_error: |
|
30
|
1 |
|
raise UnresolvableType( |
|
31
|
|
|
f"Cannot construct type {dep_type.__name__}" |
|
32
|
|
|
) from inner_error |
|
33
|
1 |
|
return None # type: ignore |
|
34
|
|
|
|
|
35
|
1 |
|
def partial(self, func: Callable[..., X], keys_to_skip=None) -> Callable[..., X]: |
|
36
|
1 |
|
spec = inspect.getfullargspec(func) |
|
37
|
1 |
|
bindable_deps = self._infer_dependencies( |
|
38
|
|
|
spec, suppress_error=True, keys_to_skip=keys_to_skip or [] |
|
39
|
|
|
) |
|
40
|
1 |
|
return functools.partial(func, **bindable_deps) |
|
41
|
|
|
|
|
42
|
1 |
|
def __getitem__(self, dep: Type[X]) -> X: |
|
43
|
1 |
|
return self.resolve(dep) |
|
44
|
|
|
|
|
45
|
1 |
|
def __setitem__(self, dep: Type, resolver: DepDefinition): |
|
46
|
1 |
|
self.define(dep, resolver) |
|
47
|
|
|
|
|
48
|
1 |
|
def _build(self, dep_type: Any) -> Any: |
|
49
|
1 |
|
if isinstance(dep_type, SpecialDepDefinition): |
|
50
|
1 |
|
return dep_type.get_instance(self._build) |
|
51
|
1 |
|
return self._reflection_build(dep_type) |
|
52
|
|
|
|
|
53
|
1 |
|
def _reflection_build(self, dep_type: Type[X]) -> X: |
|
54
|
1 |
|
spec = inspect.getfullargspec(dep_type.__init__) |
|
55
|
1 |
|
sub_deps = self._infer_dependencies(spec) |
|
56
|
1 |
|
return dep_type(**sub_deps) # type: ignore |
|
57
|
|
|
|
|
58
|
1 |
|
def _infer_dependencies( |
|
59
|
|
|
self, spec: inspect.FullArgSpec, suppress_error=False, keys_to_skip=[] |
|
60
|
|
|
): |
|
61
|
1 |
|
sub_deps = { |
|
62
|
|
|
key: self.resolve(sub_dep_type, suppress_error=suppress_error) |
|
63
|
|
|
for (key, sub_dep_type) in spec.annotations.items() |
|
64
|
|
|
if key != "return" and sub_dep_type != Any and key not in keys_to_skip |
|
65
|
|
|
} |
|
66
|
1 |
|
filtered_deps = {key: dep for (key, dep) in sub_deps.items() if dep is not None} |
|
67
|
|
|
return filtered_deps |
|
68
|
|
|
|