forked from open-feature/python-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path_hook_support.py
More file actions
143 lines (122 loc) · 4.49 KB
/
_hook_support.py
File metadata and controls
143 lines (122 loc) · 4.49 KB
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import logging
import typing
from functools import reduce
from openfeature.evaluation_context import EvaluationContext
from openfeature.flag_evaluation import FlagEvaluationDetails, FlagType
from openfeature.hook import Hook, HookContext, HookHints, HookType
logger = logging.getLogger("openfeature")
def error_hooks(
flag_type: FlagType,
exception: Exception,
hooks_and_context: list[tuple[Hook, HookContext]],
hints: HookHints | None = None,
) -> None:
kwargs = {"exception": exception, "hints": hints}
_execute_hooks(
flag_type=flag_type,
hooks_and_context=hooks_and_context,
hook_method=HookType.ERROR,
**kwargs,
)
def after_all_hooks(
flag_type: FlagType,
details: FlagEvaluationDetails[typing.Any],
hooks_and_context: list[tuple[Hook, HookContext]],
hints: HookHints | None = None,
) -> None:
kwargs = {"details": details, "hints": hints}
_execute_hooks(
flag_type=flag_type,
hooks_and_context=hooks_and_context,
hook_method=HookType.FINALLY_AFTER,
**kwargs,
)
def after_hooks(
flag_type: FlagType,
details: FlagEvaluationDetails[typing.Any],
hooks_and_context: list[tuple[Hook, HookContext]],
hints: HookHints | None = None,
) -> None:
kwargs = {"details": details, "hints": hints}
_execute_hooks_unchecked(
flag_type=flag_type,
hooks_and_context=hooks_and_context,
hook_method=HookType.AFTER,
**kwargs,
)
def before_hooks(
flag_type: FlagType,
hooks_and_context: list[tuple[Hook, HookContext]],
hints: HookHints | None = None,
) -> EvaluationContext:
kwargs = {"hints": hints}
executed_hooks = _execute_hooks_unchecked(
flag_type=flag_type,
hooks_and_context=hooks_and_context,
hook_method=HookType.BEFORE,
**kwargs,
)
filtered_hooks = [result for result in executed_hooks if result is not None]
if filtered_hooks:
return reduce(lambda a, b: a.merge(b), filtered_hooks)
return EvaluationContext()
def _execute_hooks(
flag_type: FlagType,
hooks_and_context: list[tuple[Hook, HookContext]],
hook_method: HookType,
**kwargs: typing.Any,
) -> list[EvaluationContext | None]:
"""
Run multiple hooks of any hook type. All of these hooks will be run through an
exception check.
:param flag_type: particular type of flag
:param hooks_and_context: a list of hooks and their context
:param hook_method: the type of hook that is being run
:param kwargs: arguments that need to be provided to the hook method
:return: a list of results from the applied hook methods
"""
return [
_execute_hook_checked(hook, hook_method, hook_context=hook_context, **kwargs)
for (hook, hook_context) in hooks_and_context
if hook.supports_flag_value_type(flag_type)
]
def _execute_hooks_unchecked(
flag_type: FlagType,
hooks_and_context: list[tuple[Hook, HookContext]],
hook_method: HookType,
**kwargs: typing.Any,
) -> list[EvaluationContext | None]:
"""
Execute a single hook without checking whether an exception is thrown. This is
used in the before and after hooks since any exception will be caught in the
client.
:param flag_type: particular type of flag
:param hooks_and_context: a list of hooks and their context
:param hook_method: the type of hook that is being run
:param kwargs: arguments that need to be provided to the hook method
:return: a list of results from the applied hook methods
"""
return [
getattr(hook, hook_method.value)(hook_context=hook_context, **kwargs)
for (hook, hook_context) in hooks_and_context
if hook.supports_flag_value_type(flag_type)
]
def _execute_hook_checked(
hook: Hook, hook_method: HookType, **kwargs: typing.Any
) -> EvaluationContext | None:
"""
Try and run a single hook and catch any exception thrown. This is used in the
after all and error hooks since any error thrown at this point needs to be caught.
:param hook: a list of hooks
:param hook_method: the type of hook that is being run
:param kwargs: arguments that need to be provided to the hook method
:return: the result of the hook method
"""
try:
return typing.cast(
"EvaluationContext | None",
getattr(hook, hook_method.value)(**kwargs),
)
except Exception: # pragma: no cover
logger.exception(f"Exception when running {hook_method.value} hooks")
return None