-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathinject.py
88 lines (68 loc) · 2.92 KB
/
inject.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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import logging
from typing import Any, Dict, Optional
logger = logging.getLogger(__name__)
class MagicInjectError(ValueError):
pass
def get_injection_requests(
type_hints: Dict[str, type], cname: str, component: Optional[Any] = None
) -> Dict[str, type]:
"""
Given a dict of type hints, filter it to the requested injection types.
:param type_hints: The type hints to inspect.
:param cname: The component name.
:param component: The component if it has been instantiated.
"""
requests = {}
for n, inject_type in type_hints.items():
# If the variable is private ignore it
if n.startswith("_"):
if component is None:
message = f"Cannot inject into component {cname} __init__ param {n}"
raise MagicInjectError(message)
continue
# If the variable has been set, skip it
if component is not None and hasattr(component, n):
continue
# Check for generic types from the typing module
origin = getattr(inject_type, "__origin__", None)
if origin is not None:
inject_type = origin
# If the type is not actually a type, give a meaningful error
if not isinstance(inject_type, type):
raise TypeError(
f"Component {cname} has a non-type annotation {n}: {inject_type}\n"
"Lone non-injection variable annotations are disallowed, did you want to assign a static variable?"
)
requests[n] = inject_type
return requests
def find_injections(
requests: Dict[str, type], injectables: Dict[str, Any], cname: str
) -> Dict[str, Any]:
"""
Get a dict of the variables to inject into a given component.
:param requests: The mapping of requested variables to types,
as returned by :func:`get_injection_requests`.
:param injectables: The available variables to inject.
:param cname: The name of the component.
"""
to_inject = {}
for n, inject_type in requests.items():
injectable = injectables.get(n)
if injectable is None:
# Try prefixing the component name
injectable = injectables.get(f"{cname}_{n}")
# Raise error if injectable syntax used but no injectable was found.
if injectable is None:
raise MagicInjectError(
"Component %s has variable %s (type %s), which is absent from robot"
% (cname, n, inject_type)
)
# Raise error if injectable declared with type different than the initial type
if not isinstance(injectable, inject_type):
raise MagicInjectError(
"Component %s variable %s does not match type in robot! (Got %s, expected %s)"
% (cname, n, type(injectable), inject_type)
)
to_inject[n] = injectable
logger.debug("-> %s.%s = %s", cname, n, injectable)
return to_inject