From c180405f8b082f40ee920c27e2834616bc5727ba Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Wed, 15 Nov 2023 15:07:39 -0300 Subject: [PATCH 1/2] feat: add FeatureProvider interface Signed-off-by: Federico Bond --- openfeature/api.py | 8 ++--- openfeature/client.py | 4 +-- openfeature/provider/__init__.py | 61 ++++++++++++++++++++++++++++++++ openfeature/provider/provider.py | 3 +- tests/test_api.py | 10 +++--- 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/openfeature/api.py b/openfeature/api.py index 6687bb89..92a7443e 100644 --- a/openfeature/api.py +++ b/openfeature/api.py @@ -4,11 +4,11 @@ from openfeature.evaluation_context import EvaluationContext from openfeature.exception import GeneralError from openfeature.hook import Hook +from openfeature.provider import FeatureProvider from openfeature.provider.metadata import Metadata from openfeature.provider.no_op_provider import NoOpProvider -from openfeature.provider.provider import AbstractProvider -_provider: AbstractProvider = NoOpProvider() +_provider: FeatureProvider = NoOpProvider() _evaluation_context = EvaluationContext() @@ -21,7 +21,7 @@ def get_client( return OpenFeatureClient(name=name, version=version, provider=_provider) -def set_provider(provider: AbstractProvider) -> None: +def set_provider(provider: FeatureProvider) -> None: global _provider if provider is None: raise GeneralError(error_message="No provider") @@ -31,7 +31,7 @@ def set_provider(provider: AbstractProvider) -> None: provider.initialize(_evaluation_context) -def get_provider() -> typing.Optional[AbstractProvider]: +def get_provider() -> typing.Optional[FeatureProvider]: global _provider return _provider diff --git a/openfeature/client.py b/openfeature/client.py index 4c8d8abe..c76402fb 100644 --- a/openfeature/client.py +++ b/openfeature/client.py @@ -24,8 +24,8 @@ before_hooks, error_hooks, ) +from openfeature.provider import FeatureProvider from openfeature.provider.no_op_provider import NoOpProvider -from openfeature.provider.provider import AbstractProvider logger = logging.getLogger("openfeature") @@ -69,7 +69,7 @@ def __init__( self, name: typing.Optional[str], version: typing.Optional[str], - provider: AbstractProvider, + provider: FeatureProvider, context: typing.Optional[EvaluationContext] = None, hooks: typing.Optional[typing.List[Hook]] = None, ) -> None: diff --git a/openfeature/provider/__init__.py b/openfeature/provider/__init__.py index e69de29b..25ee1f20 100644 --- a/openfeature/provider/__init__.py +++ b/openfeature/provider/__init__.py @@ -0,0 +1,61 @@ +import typing + +from openfeature.evaluation_context import EvaluationContext +from openfeature.flag_evaluation import FlagResolutionDetails +from openfeature.hook import Hook + +from .metadata import Metadata + + +class FeatureProvider(typing.Protocol): + def initialize(self, evaluation_context: EvaluationContext) -> None: + ... + + def shutdown(self) -> None: + ... + + def get_metadata(self) -> Metadata: + ... + + def get_provider_hooks(self) -> typing.List[Hook]: + ... + + def resolve_boolean_details( + self, + flag_key: str, + default_value: bool, + evaluation_context: typing.Optional[EvaluationContext] = None, + ) -> FlagResolutionDetails[bool]: + ... + + def resolve_string_details( + self, + flag_key: str, + default_value: str, + evaluation_context: typing.Optional[EvaluationContext] = None, + ) -> FlagResolutionDetails[str]: + ... + + def resolve_integer_details( + self, + flag_key: str, + default_value: int, + evaluation_context: typing.Optional[EvaluationContext] = None, + ) -> FlagResolutionDetails[int]: + ... + + def resolve_float_details( + self, + flag_key: str, + default_value: float, + evaluation_context: typing.Optional[EvaluationContext] = None, + ) -> FlagResolutionDetails[float]: + ... + + def resolve_object_details( + self, + flag_key: str, + default_value: typing.Union[dict, list], + evaluation_context: typing.Optional[EvaluationContext] = None, + ) -> FlagResolutionDetails[typing.Union[dict, list]]: + ... diff --git a/openfeature/provider/provider.py b/openfeature/provider/provider.py index 625b7f6b..7b400316 100644 --- a/openfeature/provider/provider.py +++ b/openfeature/provider/provider.py @@ -4,10 +4,11 @@ from openfeature.evaluation_context import EvaluationContext from openfeature.flag_evaluation import FlagResolutionDetails from openfeature.hook import Hook +from openfeature.provider import FeatureProvider from openfeature.provider.metadata import Metadata -class AbstractProvider: +class AbstractProvider(FeatureProvider): def initialize(self, evaluation_context: EvaluationContext) -> None: pass diff --git a/tests/test_api.py b/tests/test_api.py index 040db910..cb3cfecf 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -19,7 +19,7 @@ from openfeature.hook import Hook from openfeature.provider.metadata import Metadata from openfeature.provider.no_op_provider import NoOpProvider -from openfeature.provider.provider import AbstractProvider +from openfeature.provider.provider import FeatureProvider def test_should_not_raise_exception_with_noop_client(): @@ -61,7 +61,7 @@ def test_should_try_set_provider_and_fail_if_none_provided(): def test_should_invoke_provider_initialize_function_on_newly_registered_provider(): # Given evaluation_context = EvaluationContext("targeting_key", {"attr1": "val1"}) - provider = MagicMock(spec=AbstractProvider) + provider = MagicMock(spec=FeatureProvider) # When set_evaluation_context(evaluation_context) @@ -73,8 +73,8 @@ def test_should_invoke_provider_initialize_function_on_newly_registered_provider def test_should_invoke_provider_shutdown_function_once_provider_is_no_longer_in_use(): # Given - provider_1 = MagicMock(spec=AbstractProvider) - provider_2 = MagicMock(spec=AbstractProvider) + provider_1 = MagicMock(spec=FeatureProvider) + provider_2 = MagicMock(spec=FeatureProvider) # When set_provider(provider_1) @@ -148,7 +148,7 @@ def test_should_add_hooks_to_api_hooks(): def test_should_call_provider_shutdown_on_api_shutdown(): # Given - provider = MagicMock(spec=AbstractProvider) + provider = MagicMock(spec=FeatureProvider) set_provider(provider) # When From ec6df50a46288b716a6ebf31379b39f65ab0ae73 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Sat, 3 Feb 2024 11:21:11 -0300 Subject: [PATCH 2/2] Update openfeature/provider/__init__.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Anton GrĂ¼bel Signed-off-by: Federico Bond --- openfeature/provider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfeature/provider/__init__.py b/openfeature/provider/__init__.py index 25ee1f20..d5ddacff 100644 --- a/openfeature/provider/__init__.py +++ b/openfeature/provider/__init__.py @@ -7,7 +7,7 @@ from .metadata import Metadata -class FeatureProvider(typing.Protocol): +class FeatureProvider(typing.Protocol): # pragma: no cover def initialize(self, evaluation_context: EvaluationContext) -> None: ...