Skip to content
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
169 changes: 151 additions & 18 deletions openfeature/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
import typing
from collections.abc import Awaitable
from collections.abc import Awaitable, Sequence
from dataclasses import dataclass

from openfeature import _event_support
Expand All @@ -19,6 +19,7 @@
FlagEvaluationOptions,
FlagResolutionDetails,
FlagType,
FlagValueType,
Reason,
)
from openfeature.hook import Hook, HookContext, HookHints, get_hooks
Expand Down Expand Up @@ -342,10 +343,12 @@ async def get_float_details_async(
def get_object_value(
self,
flag_key: str,
default_value: typing.Union[dict, list],
default_value: typing.Union[
Sequence[FlagValueType], typing.Mapping[str, FlagValueType]
],
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> typing.Union[dict, list]:
) -> typing.Union[Sequence[FlagValueType], typing.Mapping[str, FlagValueType]]:
return self.get_object_details(
flag_key,
default_value,
Expand All @@ -356,10 +359,12 @@ def get_object_value(
async def get_object_value_async(
self,
flag_key: str,
default_value: typing.Union[dict, list],
default_value: typing.Union[
Sequence[FlagValueType], typing.Mapping[str, FlagValueType]
],
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> typing.Union[dict, list]:
) -> typing.Union[Sequence[FlagValueType], typing.Mapping[str, FlagValueType]]:
details = await self.get_object_details_async(
flag_key,
default_value,
Expand All @@ -371,10 +376,14 @@ async def get_object_value_async(
def get_object_details(
self,
flag_key: str,
default_value: typing.Union[dict, list],
default_value: typing.Union[
Sequence[FlagValueType], typing.Mapping[str, FlagValueType]
],
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[typing.Union[dict, list]]:
) -> FlagEvaluationDetails[
typing.Union[Sequence[FlagValueType], typing.Mapping[str, FlagValueType]]
]:
return self.evaluate_flag_details(
FlagType.OBJECT,
flag_key,
Expand All @@ -386,10 +395,14 @@ def get_object_details(
async def get_object_details_async(
self,
flag_key: str,
default_value: typing.Union[dict, list],
default_value: typing.Union[
Sequence[FlagValueType], typing.Mapping[str, FlagValueType]
],
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[typing.Union[dict, list]]:
) -> FlagEvaluationDetails[
typing.Union[Sequence[FlagValueType], typing.Mapping[str, FlagValueType]]
]:
return await self.evaluate_flag_details_async(
FlagType.OBJECT,
flag_key,
Expand All @@ -402,7 +415,7 @@ def _establish_hooks_and_provider(
self,
flag_type: FlagType,
flag_key: str,
default_value: typing.Any,
default_value: FlagValueType,
evaluation_context: typing.Optional[EvaluationContext],
flag_evaluation_options: typing.Optional[FlagEvaluationOptions],
) -> tuple[
Expand Down Expand Up @@ -479,14 +492,74 @@ def _before_hooks_and_merge_context(
)
return merged_context

@typing.overload
async def evaluate_flag_details_async(
self,
flag_type: FlagType,
flag_key: str,
default_value: bool,
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[bool]: ...

@typing.overload
async def evaluate_flag_details_async(
self,
flag_type: FlagType,
flag_key: str,
default_value: int,
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[int]: ...

@typing.overload
async def evaluate_flag_details_async(
self,
flag_type: FlagType,
flag_key: str,
default_value: float,
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[float]: ...

@typing.overload
async def evaluate_flag_details_async(
self,
flag_type: FlagType,
flag_key: str,
default_value: str,
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[str]: ...

@typing.overload
async def evaluate_flag_details_async(
self,
flag_type: FlagType,
flag_key: str,
default_value: Sequence["FlagValueType"],
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[Sequence["FlagValueType"]]: ...

@typing.overload
async def evaluate_flag_details_async(
self,
flag_type: FlagType,
flag_key: str,
default_value: typing.Mapping[str, "FlagValueType"],
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[typing.Mapping[str, "FlagValueType"]]: ...

async def evaluate_flag_details_async(
self,
flag_type: FlagType,
flag_key: str,
default_value: typing.Any,
default_value: FlagValueType,
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[typing.Any]:
) -> FlagEvaluationDetails[FlagValueType]:
"""
Evaluate the flag requested by the user from the clients provider.

Expand Down Expand Up @@ -595,14 +668,74 @@ async def evaluate_flag_details_async(
hook_hints,
)

@typing.overload
def evaluate_flag_details(
self,
flag_type: FlagType,
flag_key: str,
default_value: bool,
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[bool]: ...

@typing.overload
def evaluate_flag_details(
self,
flag_type: FlagType,
flag_key: str,
default_value: int,
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[int]: ...

@typing.overload
def evaluate_flag_details(
self,
flag_type: FlagType,
flag_key: str,
default_value: float,
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[float]: ...

@typing.overload
def evaluate_flag_details(
self,
flag_type: FlagType,
flag_key: str,
default_value: str,
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[str]: ...

@typing.overload
def evaluate_flag_details(
self,
flag_type: FlagType,
flag_key: str,
default_value: Sequence["FlagValueType"],
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[Sequence["FlagValueType"]]: ...

@typing.overload
def evaluate_flag_details(
self,
flag_type: FlagType,
flag_key: str,
default_value: typing.Mapping[str, "FlagValueType"],
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[typing.Mapping[str, "FlagValueType"]]: ...

def evaluate_flag_details(
self,
flag_type: FlagType,
flag_key: str,
default_value: typing.Any,
default_value: FlagValueType,
evaluation_context: typing.Optional[EvaluationContext] = None,
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
) -> FlagEvaluationDetails[typing.Any]:
) -> FlagEvaluationDetails[FlagValueType]:
"""
Evaluate the flag requested by the user from the clients provider.

Expand Down Expand Up @@ -718,9 +851,9 @@ async def _create_provider_evaluation_async(
provider: FeatureProvider,
flag_type: FlagType,
flag_key: str,
default_value: typing.Any,
default_value: FlagValueType,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagEvaluationDetails[typing.Any]:
) -> FlagEvaluationDetails[FlagValueType]:
get_details_callables_async: typing.Mapping[
FlagType, ResolveDetailsCallableAsync
] = {
Expand Down Expand Up @@ -765,9 +898,9 @@ def _create_provider_evaluation(
provider: FeatureProvider,
flag_type: FlagType,
flag_key: str,
default_value: typing.Any,
default_value: FlagValueType,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagEvaluationDetails[typing.Any]:
) -> FlagEvaluationDetails[FlagValueType]:
"""
Encapsulated method to create a FlagEvaluationDetail from a specific provider.

Expand Down
18 changes: 17 additions & 1 deletion openfeature/evaluation_context/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
from __future__ import annotations

import typing
from collections.abc import Sequence
from dataclasses import dataclass, field
from datetime import datetime

from openfeature.exception import GeneralError

__all__ = ["EvaluationContext", "get_evaluation_context", "set_evaluation_context"]

# https://openfeature.dev/specification/sections/evaluation-context#requirement-312
EvaluationContextAttributes = typing.Mapping[
str,
typing.Union[
bool,
int,
float,
str,
datetime,
Sequence["EvaluationContextAttributes"],
typing.Mapping[str, "EvaluationContextAttributes"],
],
]


@dataclass
class EvaluationContext:
targeting_key: typing.Optional[str] = None
attributes: dict = field(default_factory=dict)
attributes: EvaluationContextAttributes = field(default_factory=dict)

def merge(self, ctx2: EvaluationContext) -> EvaluationContext:
if not (self and ctx2):
Expand Down
11 changes: 10 additions & 1 deletion openfeature/flag_evaluation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import typing
from collections.abc import Sequence
from dataclasses import dataclass, field

from openfeature._backports.strenum import StrEnum
Expand Down Expand Up @@ -41,7 +42,15 @@ class Reason(StrEnum):
UNKNOWN = "UNKNOWN"


FlagMetadata = typing.Mapping[str, typing.Any]
FlagMetadata = typing.Mapping[str, typing.Union[bool, int, float, str]]
FlagValueType = typing.Union[
bool,
int,
float,
str,
Sequence["FlagValueType"],
typing.Mapping[str, "FlagValueType"],
]

T_co = typing.TypeVar("T_co", covariant=True)

Expand Down
13 changes: 7 additions & 6 deletions openfeature/hook/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from __future__ import annotations

import typing
from collections.abc import Sequence
from datetime import datetime
from enum import Enum
from typing import TYPE_CHECKING

from openfeature.evaluation_context import EvaluationContext
from openfeature.flag_evaluation import FlagEvaluationDetails, FlagType
from openfeature.flag_evaluation import FlagEvaluationDetails, FlagType, FlagValueType

if TYPE_CHECKING:
from openfeature.client import ClientMetadata
Expand Down Expand Up @@ -37,7 +38,7 @@ def __init__(
self,
flag_key: str,
flag_type: FlagType,
default_value: typing.Any,
default_value: FlagValueType,
evaluation_context: EvaluationContext,
client_metadata: typing.Optional[ClientMetadata] = None,
provider_metadata: typing.Optional[Metadata] = None,
Expand Down Expand Up @@ -70,8 +71,8 @@ def __setattr__(self, key: str, value: typing.Any) -> None:
float,
str,
datetime,
list[typing.Any],
dict[str, typing.Any],
Sequence["HookHints"],
typing.Mapping[str, "HookHints"],
],
]

Expand All @@ -94,7 +95,7 @@ def before(
def after(
self,
hook_context: HookContext,
details: FlagEvaluationDetails[typing.Any],
details: FlagEvaluationDetails[FlagValueType],
hints: HookHints,
) -> None:
"""
Expand Down Expand Up @@ -122,7 +123,7 @@ def error(
def finally_after(
self,
hook_context: HookContext,
details: FlagEvaluationDetails[typing.Any],
details: FlagEvaluationDetails[FlagValueType],
hints: HookHints,
) -> None:
"""
Expand Down
Loading