Skip to content

refactor!: move AbstractProvider to openfeature.provider #314

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 1 commit into from
Apr 12, 2024
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
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,7 @@ from typing import List, Optional, Union
from openfeature.evaluation_context import EvaluationContext
from openfeature.flag_evaluation import FlagResolutionDetails
from openfeature.hook import Hook
from openfeature.provider.metadata import Metadata
from openfeature.provider.provider import AbstractProvider
from openfeature.provider import AbstractProvider, Metadata

class MyProvider(AbstractProvider):
def get_metadata(self) -> Metadata:
Expand Down
10 changes: 8 additions & 2 deletions openfeature/_event_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
ProviderEvent,
ProviderEventDetails,
)
from openfeature.provider import FeatureProvider
from openfeature.provider import FeatureProvider, ProviderStatus

if TYPE_CHECKING:
from openfeature.client import OpenFeatureClient
Expand Down Expand Up @@ -80,7 +80,13 @@ def run_handlers_for_provider(
def _run_immediate_handler(
client: OpenFeatureClient, event: ProviderEvent, handler: EventHandler
) -> None:
if event == ProviderEvent.from_provider_status(client.get_provider_status()):
status_to_event = {
ProviderStatus.READY: ProviderEvent.PROVIDER_READY,
ProviderStatus.ERROR: ProviderEvent.PROVIDER_ERROR,
ProviderStatus.FATAL: ProviderEvent.PROVIDER_ERROR,
ProviderStatus.STALE: ProviderEvent.PROVIDER_STALE,
}
if event == status_to_event.get(client.get_provider_status()):
handler(EventDetails(provider_name=client.provider.get_metadata().name))


Expand Down
15 changes: 1 addition & 14 deletions openfeature/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

from dataclasses import dataclass, field
from enum import Enum
from typing import Callable, ClassVar, Dict, List, Optional, Union
from typing import Callable, Dict, List, Optional, Union

from openfeature.exception import ErrorCode
from openfeature.provider import ProviderStatus

__all__ = ["ProviderEvent", "ProviderEventDetails", "EventDetails", "EventHandler"]

Expand All @@ -16,18 +15,6 @@ class ProviderEvent(Enum):
PROVIDER_ERROR = "PROVIDER_ERROR"
PROVIDER_STALE = "PROVIDER_STALE"

__status__: ClassVar[Dict[ProviderStatus, str]] = {
ProviderStatus.READY: PROVIDER_READY,
ProviderStatus.ERROR: PROVIDER_ERROR,
ProviderStatus.FATAL: PROVIDER_ERROR,
ProviderStatus.STALE: PROVIDER_STALE,
}

@classmethod
def from_provider_status(cls, status: ProviderStatus) -> Optional[ProviderEvent]:
value = ProviderEvent.__status__.get(status)
return ProviderEvent[value] if value else None


@dataclass
class ProviderEventDetails:
Expand Down
85 changes: 84 additions & 1 deletion openfeature/provider/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
from __future__ import annotations

import typing
from abc import abstractmethod
from enum import Enum

from openfeature.evaluation_context import EvaluationContext
from openfeature.event import ProviderEvent, ProviderEventDetails
from openfeature.flag_evaluation import FlagResolutionDetails
from openfeature.hook import Hook

from .metadata import Metadata

__all__ = ["ProviderStatus", "FeatureProvider", "Metadata"]
__all__ = ["AbstractProvider", "ProviderStatus", "FeatureProvider", "Metadata"]


class ProviderStatus(Enum):
Expand Down Expand Up @@ -61,3 +65,82 @@
default_value: typing.Union[dict, list],
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[typing.Union[dict, list]]: ...


class AbstractProvider(FeatureProvider):
def initialize(self, evaluation_context: EvaluationContext) -> None:
pass

def shutdown(self) -> None:
pass

@abstractmethod
def get_metadata(self) -> Metadata:
pass

Check warning on line 79 in openfeature/provider/__init__.py

View check run for this annotation

Codecov / codecov/patch

openfeature/provider/__init__.py#L79

Added line #L79 was not covered by tests

def get_provider_hooks(self) -> typing.List[Hook]:
return []

Check warning on line 82 in openfeature/provider/__init__.py

View check run for this annotation

Codecov / codecov/patch

openfeature/provider/__init__.py#L82

Added line #L82 was not covered by tests

@abstractmethod
def resolve_boolean_details(
self,
flag_key: str,
default_value: bool,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[bool]:
pass

Check warning on line 91 in openfeature/provider/__init__.py

View check run for this annotation

Codecov / codecov/patch

openfeature/provider/__init__.py#L91

Added line #L91 was not covered by tests

@abstractmethod
def resolve_string_details(
self,
flag_key: str,
default_value: str,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[str]:
pass

Check warning on line 100 in openfeature/provider/__init__.py

View check run for this annotation

Codecov / codecov/patch

openfeature/provider/__init__.py#L100

Added line #L100 was not covered by tests

@abstractmethod
def resolve_integer_details(
self,
flag_key: str,
default_value: int,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[int]:
pass

Check warning on line 109 in openfeature/provider/__init__.py

View check run for this annotation

Codecov / codecov/patch

openfeature/provider/__init__.py#L109

Added line #L109 was not covered by tests

@abstractmethod
def resolve_float_details(
self,
flag_key: str,
default_value: float,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[float]:
pass

Check warning on line 118 in openfeature/provider/__init__.py

View check run for this annotation

Codecov / codecov/patch

openfeature/provider/__init__.py#L118

Added line #L118 was not covered by tests

@abstractmethod
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]]:
pass

Check warning on line 127 in openfeature/provider/__init__.py

View check run for this annotation

Codecov / codecov/patch

openfeature/provider/__init__.py#L127

Added line #L127 was not covered by tests

def emit_provider_ready(self, details: ProviderEventDetails) -> None:
self.emit(ProviderEvent.PROVIDER_READY, details)

def emit_provider_configuration_changed(
self, details: ProviderEventDetails
) -> None:
self.emit(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, details)

def emit_provider_error(self, details: ProviderEventDetails) -> None:
self.emit(ProviderEvent.PROVIDER_ERROR, details)

def emit_provider_stale(self, details: ProviderEventDetails) -> None:
self.emit(ProviderEvent.PROVIDER_STALE, details)

def emit(self, event: ProviderEvent, details: ProviderEventDetails) -> None:
from openfeature.provider._registry import provider_registry

provider_registry.dispatch_event(self, event, details)
3 changes: 1 addition & 2 deletions openfeature/provider/in_memory_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
from openfeature.exception import FlagNotFoundError
from openfeature.flag_evaluation import FlagMetadata, FlagResolutionDetails, Reason
from openfeature.hook import Hook
from openfeature.provider.metadata import Metadata
from openfeature.provider.provider import AbstractProvider
from openfeature.provider import AbstractProvider, Metadata

PASSED_IN_DEFAULT = "Passed in default"

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

PASSED_IN_DEFAULT = "Passed in default"

Expand Down
93 changes: 7 additions & 86 deletions openfeature/provider/provider.py
Original file line number Diff line number Diff line change
@@ -1,90 +1,11 @@
import typing
from abc import abstractmethod
import warnings

from openfeature.evaluation_context import EvaluationContext
from openfeature.event import ProviderEvent, ProviderEventDetails
from openfeature.flag_evaluation import FlagResolutionDetails
from openfeature.hook import Hook
from openfeature.provider import FeatureProvider
from openfeature.provider.metadata import Metadata
from openfeature.provider import AbstractProvider

__all__ = ["AbstractProvider"]


class AbstractProvider(FeatureProvider):
def initialize(self, evaluation_context: EvaluationContext) -> None:
pass

def shutdown(self) -> None:
pass

@abstractmethod
def get_metadata(self) -> Metadata:
pass

def get_provider_hooks(self) -> typing.List[Hook]:
return []

@abstractmethod
def resolve_boolean_details(
self,
flag_key: str,
default_value: bool,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[bool]:
pass

@abstractmethod
def resolve_string_details(
self,
flag_key: str,
default_value: str,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[str]:
pass

@abstractmethod
def resolve_integer_details(
self,
flag_key: str,
default_value: int,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[int]:
pass

@abstractmethod
def resolve_float_details(
self,
flag_key: str,
default_value: float,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[float]:
pass

@abstractmethod
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]]:
pass

def emit_provider_ready(self, details: ProviderEventDetails) -> None:
self.emit(ProviderEvent.PROVIDER_READY, details)

def emit_provider_configuration_changed(
self, details: ProviderEventDetails
) -> None:
self.emit(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, details)

def emit_provider_error(self, details: ProviderEventDetails) -> None:
self.emit(ProviderEvent.PROVIDER_ERROR, details)

def emit_provider_stale(self, details: ProviderEventDetails) -> None:
self.emit(ProviderEvent.PROVIDER_STALE, details)

def emit(self, event: ProviderEvent, details: ProviderEventDetails) -> None:
from openfeature.provider._registry import provider_registry

provider_registry.dispatch_event(self, event, details)
warnings.warn(
"openfeature.provider.provider is deprecated, use openfeature.provider instead",
DeprecationWarning,
stacklevel=1,
)