Skip to content

fix: Hook methods should have default non-abstract implementations #216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions openfeature/evaluation_context.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import typing
from dataclasses import dataclass, field


@dataclass
class EvaluationContext:
def __init__(
self,
targeting_key: typing.Optional[str] = None,
attributes: typing.Optional[dict] = None,
):
self.targeting_key = targeting_key
self.attributes = attributes or {}
targeting_key: typing.Optional[str] = None
attributes: dict = field(default_factory=dict)

def merge(self, ctx2: "EvaluationContext") -> "EvaluationContext":
if not (self and ctx2):
Expand Down
12 changes: 4 additions & 8 deletions openfeature/hook/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations

import typing
from abc import abstractmethod
from dataclasses import dataclass
from enum import Enum

Expand All @@ -27,8 +26,9 @@ class HookContext:


class Hook:
@abstractmethod
def before(self, hook_context: HookContext, hints: dict) -> EvaluationContext:
def before(
self, hook_context: HookContext, hints: dict
) -> typing.Optional[EvaluationContext]:
"""
Runs before flag is resolved.

Expand All @@ -38,9 +38,8 @@ def before(self, hook_context: HookContext, hints: dict) -> EvaluationContext:
:return: An EvaluationContext. It will be merged with the
EvaluationContext instances from other hooks, the client and API.
"""
pass
return None

@abstractmethod
def after(
self, hook_context: HookContext, details: FlagEvaluationDetails, hints: dict
):
Expand All @@ -54,7 +53,6 @@ def after(
"""
pass

@abstractmethod
def error(self, hook_context: HookContext, exception: Exception, hints: dict):
"""
Run when evaluation encounters an error. Errors thrown will be swallowed.
Expand All @@ -65,7 +63,6 @@ def error(self, hook_context: HookContext, exception: Exception, hints: dict):
"""
pass

@abstractmethod
def finally_after(self, hook_context: HookContext, hints: dict):
"""
Run after flag evaluation, including any error processing.
Expand All @@ -76,7 +73,6 @@ def finally_after(self, hook_context: HookContext, hints: dict):
"""
pass

@abstractmethod
def supports_flag_value_type(self, flag_type: FlagType) -> bool:
"""
Check to see if the hook supports the particular flag type.
Expand Down
22 changes: 20 additions & 2 deletions tests/hook/test_hook_support.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from unittest.mock import ANY
from unittest.mock import ANY, MagicMock

from openfeature.evaluation_context import EvaluationContext
from openfeature.flag_evaluation import FlagEvaluationDetails, FlagType
from openfeature.hook import HookContext
from openfeature.hook import Hook, HookContext
from openfeature.hook.hook_support import (
after_all_hooks,
after_hooks,
Expand Down Expand Up @@ -37,6 +38,23 @@ def test_before_hooks_run_before_method(mock_hook):
mock_hook.before.assert_called_with(hook_context=hook_context, hints=hook_hints)


def test_before_hooks_merges_evaluation_contexts():
# Given
hook_context = HookContext("flag_key", FlagType.BOOLEAN, True, "")
hook_1 = MagicMock(spec=Hook)
hook_1.before.return_value = EvaluationContext("foo", {"key_1": "val_1"})
hook_2 = MagicMock(spec=Hook)
hook_2.before.return_value = EvaluationContext("bar", {"key_2": "val_2"})
hook_3 = MagicMock(spec=Hook)
hook_3.before.return_value = None

# When
context = before_hooks(FlagType.BOOLEAN, hook_context, [hook_1, hook_2, hook_3])

# Then
assert context == EvaluationContext("bar", {"key_1": "val_1", "key_2": "val_2"})


def test_after_hooks_run_after_method(mock_hook):
# Given
hook_context = HookContext("flag_key", FlagType.BOOLEAN, True, "")
Expand Down