Skip to content

Commit

Permalink
refactor!: rename open_feature.hooks module to hook
Browse files Browse the repository at this point in the history
Signed-off-by: Federico Bond <federicobond@gmail.com>
  • Loading branch information
federicobond committed Sep 1, 2023
1 parent e7ee6b3 commit a99328e
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from open_feature.evaluation_context import EvaluationContext
from open_feature.flag_evaluation import FlagEvaluationDetails, FlagType
from open_feature.hooks import Hook, HookContext, HookType
from open_feature.hook import Hook, HookContext, HookType


def error_hooks(
Expand Down
2 changes: 1 addition & 1 deletion open_feature/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from open_feature.client import OpenFeatureClient
from open_feature.evaluation_context import EvaluationContext
from open_feature.exception import GeneralError
from open_feature.hooks import Hook
from open_feature.hook import Hook
from open_feature.provider.metadata import Metadata
from open_feature.provider.no_op_provider import NoOpProvider
from open_feature.provider.provider import AbstractProvider
Expand Down
4 changes: 2 additions & 2 deletions open_feature/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
Reason,
FlagResolutionDetails,
)
from open_feature.hooks import Hook, HookContext
from open_feature.hooks.hook_support import (
from open_feature.hook import Hook, HookContext
from open_feature.hook.hook_support import (
after_all_hooks,
after_hooks,
before_hooks,
Expand Down
2 changes: 1 addition & 1 deletion open_feature/flag_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from open_feature.exception import ErrorCode

if typing.TYPE_CHECKING: # resolves a circular dependency in type annotations
from open_feature.hooks import Hook
from open_feature.hook import Hook


class FlagType(StrEnum):
Expand Down
File renamed without changes.
130 changes: 130 additions & 0 deletions open_feature/hook/hook_support.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import logging
import typing
from functools import reduce

from open_feature.evaluation_context import EvaluationContext
from open_feature.flag_evaluation import FlagEvaluationDetails, FlagType
from open_feature.hook import Hook, HookContext, HookType


def error_hooks(
flag_type: FlagType,
hook_context: HookContext,
exception: Exception,
hooks: typing.List[Hook],
hints: typing.Optional[typing.Mapping] = None,
):
kwargs = {"hook_context": hook_context, "exception": exception, "hints": hints}
_execute_hooks(
flag_type=flag_type, hooks=hooks, hook_method=HookType.ERROR, **kwargs
)


def after_all_hooks(
flag_type: FlagType,
hook_context: HookContext,
hooks: typing.List[Hook],
hints: typing.Optional[typing.Mapping] = None,
):
kwargs = {"hook_context": hook_context, "hints": hints}
_execute_hooks(
flag_type=flag_type, hooks=hooks, hook_method=HookType.FINALLY_AFTER, **kwargs
)


def after_hooks(
flag_type: FlagType,
hook_context: HookContext,
details: FlagEvaluationDetails,
hooks: typing.List[Hook],
hints: typing.Optional[typing.Mapping] = None,
):
kwargs = {"hook_context": hook_context, "details": details, "hints": hints}
_execute_hooks_unchecked(
flag_type=flag_type, hooks=hooks, hook_method=HookType.AFTER, **kwargs
)


def before_hooks(
flag_type: FlagType,
hook_context: HookContext,
hooks: typing.List[Hook],
hints: typing.Optional[typing.Mapping] = None,
) -> EvaluationContext:
kwargs = {"hook_context": hook_context, "hints": hints}
executed_hooks = _execute_hooks_unchecked(
flag_type=flag_type, hooks=hooks, hook_method=HookType.BEFORE, **kwargs
)
filtered_hooks = list(filter(lambda hook: hook is not None, executed_hooks))

if filtered_hooks:
return reduce(lambda a, b: a.merge(b), filtered_hooks)

return EvaluationContext()


def _execute_hooks(
flag_type: FlagType, hooks: typing.List[Hook], hook_method: HookType, **kwargs
) -> list:
"""
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: 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: a list of results from the applied hook methods
"""
if hooks:
filtered_hooks = list(
filter(
lambda hook: hook.supports_flag_value_type(flag_type=flag_type), hooks
)
)
return [
_execute_hook_checked(hook, hook_method, **kwargs)
for hook in filtered_hooks
]
return []


def _execute_hooks_unchecked(
flag_type: FlagType, hooks, hook_method: HookType, **kwargs
) -> list:
"""
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: 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: a list of results from the applied hook methods
"""
if hooks:
filtered_hooks = list(
filter(
lambda hook: hook.supports_flag_value_type(flag_type=flag_type), hooks
)
)
return [getattr(hook, hook_method.value)(**kwargs) for hook in filtered_hooks]

return []


def _execute_hook_checked(hook: Hook, hook_method: HookType, **kwargs):
"""
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 getattr(hook, hook_method.value)(**kwargs)
except Exception: # noqa
logging.error(f"Exception when running {hook_method.value} hooks")
2 changes: 1 addition & 1 deletion open_feature/provider/in_memory_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from open_feature.evaluation_context import EvaluationContext
from open_feature.exception import ErrorCode
from open_feature.flag_evaluation import FlagResolutionDetails, Reason
from open_feature.hooks import Hook
from open_feature.hook import Hook
from open_feature.provider.metadata import Metadata
from open_feature.provider.provider import AbstractProvider

Expand Down
2 changes: 1 addition & 1 deletion open_feature/provider/no_op_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from open_feature.evaluation_context import EvaluationContext
from open_feature.flag_evaluation import FlagResolutionDetails, Reason
from open_feature.hooks import Hook
from open_feature.hook import Hook
from open_feature.provider.metadata import Metadata
from open_feature.provider.no_op_metadata import NoOpMetadata
from open_feature.provider.provider import AbstractProvider
Expand Down
2 changes: 1 addition & 1 deletion open_feature/provider/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from open_feature.evaluation_context import EvaluationContext
from open_feature.flag_evaluation import FlagResolutionDetails
from open_feature.hooks import Hook
from open_feature.hook import Hook
from open_feature.provider.metadata import Metadata


Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ See [here](https://openfeature.dev/ecosystem) for a catalog of available provide
A hook is a mechanism that allows for adding arbitrary behavior at well-defined points of the flag evaluation life-cycle. Use cases include validating the resolved flag value, modifying or adding data to the evaluation context, logging, telemetry, and tracking.

```python
from open_feature.hooks import Hook
from open_feature.hook import Hook

class MyHook(Hook):
def after(self, hook_context: HookContext, details: FlagEvaluationDetails, hints: dict):
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from unittest.mock import ANY

from open_feature.flag_evaluation import FlagEvaluationDetails, FlagType
from open_feature.hooks import HookContext
from open_feature.hooks.hook_support import (
from open_feature.hook import HookContext
from open_feature.hook.hook_support import (
after_all_hooks,
after_hooks,
before_hooks,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
)
from open_feature.evaluation_context import EvaluationContext
from open_feature.exception import ErrorCode, GeneralError
from open_feature.hooks import Hook
from open_feature.hook import Hook
from open_feature.provider.metadata import Metadata
from open_feature.provider.no_op_provider import NoOpProvider

Expand Down
2 changes: 1 addition & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from open_feature.client import OpenFeatureClient
from open_feature.exception import ErrorCode, OpenFeatureError
from open_feature.flag_evaluation import Reason
from open_feature.hooks import Hook
from open_feature.hook import Hook
from open_feature.provider.no_op_provider import NoOpProvider


Expand Down

0 comments on commit a99328e

Please sign in to comment.