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
10 changes: 3 additions & 7 deletions openfeature/api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import typing

from openfeature import _event_support
from openfeature.client import OpenFeatureClient
from openfeature.evaluation_context import (
Expand Down Expand Up @@ -40,14 +38,12 @@


def get_client(
domain: typing.Optional[str] = None, version: typing.Optional[str] = None
domain: str | None = None, version: str | None = None
) -> OpenFeatureClient:
return OpenFeatureClient(domain=domain, version=version)


def set_provider(
provider: FeatureProvider, domain: typing.Optional[str] = None
) -> None:
def set_provider(provider: FeatureProvider, domain: str | None = None) -> None:
if domain is None:
provider_registry.set_default_provider(provider)
else:
Expand All @@ -59,7 +55,7 @@ def clear_providers() -> None:
_event_support.clear()


def get_provider_metadata(domain: typing.Optional[str] = None) -> Metadata:
def get_provider_metadata(domain: str | None = None) -> Metadata:
return provider_registry.get_provider(domain).get_metadata()


Expand Down
202 changes: 92 additions & 110 deletions openfeature/client.py

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions openfeature/evaluation_context/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@
__all__ = ["EvaluationContext", "get_evaluation_context", "set_evaluation_context"]

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


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

def merge(self, ctx2: EvaluationContext) -> EvaluationContext:
Expand Down
21 changes: 8 additions & 13 deletions openfeature/event.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import typing
from collections.abc import Callable
from dataclasses import dataclass, field
from enum import Enum
Expand All @@ -19,23 +18,19 @@ class ProviderEvent(Enum):

@dataclass
class ProviderEventDetails:
flags_changed: typing.Optional[list[str]] = None
message: typing.Optional[str] = None
error_code: typing.Optional[ErrorCode] = None
metadata: dict[str, typing.Union[bool, str, int, float]] = field(
default_factory=dict
)
flags_changed: list[str] | None = None
message: str | None = None
error_code: ErrorCode | None = None
metadata: dict[str, bool | str | int | float] = field(default_factory=dict)


@dataclass
class EventDetails(ProviderEventDetails):
provider_name: str = ""
flags_changed: typing.Optional[list[str]] = None
message: typing.Optional[str] = None
error_code: typing.Optional[ErrorCode] = None
metadata: dict[str, typing.Union[bool, str, int, float]] = field(
default_factory=dict
)
flags_changed: list[str] | None = None
message: str | None = None
error_code: ErrorCode | None = None
metadata: dict[str, bool | str | int | float] = field(default_factory=dict)

@classmethod
def from_provider_event_details(
Expand Down
21 changes: 9 additions & 12 deletions openfeature/exception.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import typing
from collections.abc import Callable, Mapping

from openfeature._backports.strenum import StrEnum
Expand All @@ -25,9 +24,7 @@ class OpenFeatureError(Exception):
the more specific exceptions extending this one should be used.
"""

def __init__(
self, error_code: ErrorCode, error_message: typing.Optional[str] = None
):
def __init__(self, error_code: ErrorCode, error_message: str | None = None):
"""
Constructor for the generic OpenFeatureError.
@param error_message: an optional string message representing why the
Expand All @@ -43,7 +40,7 @@ class ProviderNotReadyError(OpenFeatureError):
This exception should be raised when the provider is not ready to be used.
"""

def __init__(self, error_message: typing.Optional[str] = None):
def __init__(self, error_message: str | None = None):
"""
Constructor for the ProviderNotReadyError. The error code for this type of
exception is ErrorCode.PROVIDER_NOT_READY.
Expand All @@ -58,7 +55,7 @@ class ProviderFatalError(OpenFeatureError):
This exception should be raised when the provider encounters a fatal error.
"""

def __init__(self, error_message: typing.Optional[str] = None):
def __init__(self, error_message: str | None = None):
"""
Constructor for the ProviderFatalError. The error code for this type of
exception is ErrorCode.PROVIDER_FATAL.
Expand All @@ -74,7 +71,7 @@ class FlagNotFoundError(OpenFeatureError):
key provided by the user.
"""

def __init__(self, error_message: typing.Optional[str] = None):
def __init__(self, error_message: str | None = None):
"""
Constructor for the FlagNotFoundError. The error code for
this type of exception is ErrorCode.FLAG_NOT_FOUND.
Expand All @@ -90,7 +87,7 @@ class GeneralError(OpenFeatureError):
feature python sdk.
"""

def __init__(self, error_message: typing.Optional[str] = None):
def __init__(self, error_message: str | None = None):
"""
Constructor for the GeneralError. The error code for this type of exception
is ErrorCode.GENERAL.
Expand All @@ -106,7 +103,7 @@ class ParseError(OpenFeatureError):
be parsed into a FlagEvaluationDetails object.
"""

def __init__(self, error_message: typing.Optional[str] = None):
def __init__(self, error_message: str | None = None):
"""
Constructor for the ParseError. The error code for this type of exception
is ErrorCode.PARSE_ERROR.
Expand All @@ -122,7 +119,7 @@ class TypeMismatchError(OpenFeatureError):
not match the type requested by the user.
"""

def __init__(self, error_message: typing.Optional[str] = None):
def __init__(self, error_message: str | None = None):
"""
Constructor for the TypeMismatchError. The error code for this type of
exception is ErrorCode.TYPE_MISMATCH.
Expand All @@ -138,7 +135,7 @@ class TargetingKeyMissingError(OpenFeatureError):
but one was not provided in the evaluation context.
"""

def __init__(self, error_message: typing.Optional[str] = None):
def __init__(self, error_message: str | None = None):
"""
Constructor for the TargetingKeyMissingError. The error code for this type of
exception is ErrorCode.TARGETING_KEY_MISSING.
Expand All @@ -154,7 +151,7 @@ class InvalidContextError(OpenFeatureError):
requirements.
"""

def __init__(self, error_message: typing.Optional[str]):
def __init__(self, error_message: str | None):
"""
Constructor for the InvalidContextError. The error code for this type of
exception is ErrorCode.INVALID_CONTEXT.
Expand Down
31 changes: 13 additions & 18 deletions openfeature/flag_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,10 @@ class Reason(StrEnum):
UNKNOWN = "UNKNOWN"


FlagMetadata = Mapping[str, typing.Union[bool, int, float, str]]
FlagValueType = typing.Union[
bool,
int,
float,
str,
Sequence["FlagValueType"],
Mapping[str, "FlagValueType"],
]
FlagMetadata = Mapping[str, bool | int | float | str]
FlagValueType: typing.TypeAlias = (
bool | int | float | str | Sequence["FlagValueType"] | Mapping[str, "FlagValueType"]
)

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

Expand All @@ -59,13 +54,13 @@ class Reason(StrEnum):
class FlagEvaluationDetails(typing.Generic[T_co]):
flag_key: str
value: T_co
variant: typing.Optional[str] = None
variant: str | None = None
flag_metadata: FlagMetadata = field(default_factory=dict)
reason: typing.Optional[typing.Union[str, Reason]] = None
error_code: typing.Optional[ErrorCode] = None
error_message: typing.Optional[str] = None
reason: str | Reason | None = None
error_code: ErrorCode | None = None
error_message: str | None = None

def get_exception(self) -> typing.Optional[OpenFeatureError]:
def get_exception(self) -> OpenFeatureError | None:
if self.error_code:
return ErrorCode.to_exception(self.error_code, self.error_message or "")
return None
Expand All @@ -83,10 +78,10 @@ class FlagEvaluationOptions:
@dataclass
class FlagResolutionDetails(typing.Generic[U_co]):
value: U_co
error_code: typing.Optional[ErrorCode] = None
error_message: typing.Optional[str] = None
reason: typing.Optional[typing.Union[str, Reason]] = None
variant: typing.Optional[str] = None
error_code: ErrorCode | None = None
error_message: str | None = None
reason: str | Reason | None = None
variant: str | None = None
flag_metadata: FlagMetadata = field(default_factory=dict)

def raise_for_error(self) -> None:
Expand Down
26 changes: 13 additions & 13 deletions openfeature/hook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ def __init__( # noqa: PLR0913
flag_type: FlagType,
default_value: FlagValueType,
evaluation_context: EvaluationContext,
client_metadata: typing.Optional[ClientMetadata] = None,
provider_metadata: typing.Optional[Metadata] = None,
hook_data: typing.Optional[HookData] = None,
client_metadata: ClientMetadata | None = None,
provider_metadata: Metadata | None = None,
hook_data: HookData | None = None,
):
self.flag_key = flag_key
self.flag_type = flag_type
Expand All @@ -69,23 +69,23 @@ def __setattr__(self, key: str, value: typing.Any) -> None:


# https://openfeature.dev/specification/sections/hooks/#requirement-421
HookHintValue = typing.Union[
bool,
int,
float,
str,
datetime,
Sequence["HookHintValue"],
Mapping[str, "HookHintValue"],
]
HookHintValue: typing.TypeAlias = (
bool
| int
| float
| str
| datetime
| Sequence["HookHintValue"]
| Mapping[str, "HookHintValue"]
)

HookHints = Mapping[str, HookHintValue]


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

Expand Down
16 changes: 8 additions & 8 deletions openfeature/hook/_hook_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def error_hooks(
flag_type: FlagType,
exception: Exception,
hooks_and_context: list[tuple[Hook, HookContext]],
hints: typing.Optional[HookHints] = None,
hints: HookHints | None = None,
) -> None:
kwargs = {"exception": exception, "hints": hints}
_execute_hooks(
Expand All @@ -28,7 +28,7 @@ def after_all_hooks(
flag_type: FlagType,
details: FlagEvaluationDetails[typing.Any],
hooks_and_context: list[tuple[Hook, HookContext]],
hints: typing.Optional[HookHints] = None,
hints: HookHints | None = None,
) -> None:
kwargs = {"details": details, "hints": hints}
_execute_hooks(
Expand All @@ -43,7 +43,7 @@ def after_hooks(
flag_type: FlagType,
details: FlagEvaluationDetails[typing.Any],
hooks_and_context: list[tuple[Hook, HookContext]],
hints: typing.Optional[HookHints] = None,
hints: HookHints | None = None,
) -> None:
kwargs = {"details": details, "hints": hints}
_execute_hooks_unchecked(
Expand All @@ -57,7 +57,7 @@ def after_hooks(
def before_hooks(
flag_type: FlagType,
hooks_and_context: list[tuple[Hook, HookContext]],
hints: typing.Optional[HookHints] = None,
hints: HookHints | None = None,
) -> EvaluationContext:
kwargs = {"hints": hints}
executed_hooks = _execute_hooks_unchecked(
Expand All @@ -79,7 +79,7 @@ def _execute_hooks(
hooks_and_context: list[tuple[Hook, HookContext]],
hook_method: HookType,
**kwargs: typing.Any,
) -> list[typing.Optional[EvaluationContext]]:
) -> list[EvaluationContext | None]:
"""
Run multiple hooks of any hook type. All of these hooks will be run through an
exception check.
Expand All @@ -102,7 +102,7 @@ def _execute_hooks_unchecked(
hooks_and_context: list[tuple[Hook, HookContext]],
hook_method: HookType,
**kwargs: typing.Any,
) -> list[typing.Optional[EvaluationContext]]:
) -> list[EvaluationContext | None]:
"""
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
Expand All @@ -123,7 +123,7 @@ def _execute_hooks_unchecked(

def _execute_hook_checked(
hook: Hook, hook_method: HookType, **kwargs: typing.Any
) -> typing.Optional[EvaluationContext]:
) -> EvaluationContext | None:
"""
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.
Expand All @@ -135,7 +135,7 @@ def _execute_hook_checked(
"""
try:
return typing.cast(
"typing.Optional[EvaluationContext]",
"EvaluationContext | None",
getattr(hook, hook_method.value)(**kwargs),
)
except Exception: # pragma: no cover
Expand Down
Loading