Skip to content

Commit 7102813

Browse files
committed
Rework typing
Signed-off-by: Manuel Schönlaub <[email protected]>
1 parent 860d42d commit 7102813

File tree

8 files changed

+94
-73
lines changed

8 files changed

+94
-73
lines changed

open_feature/evaluation_context/evaluation_context.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
import typing
2+
3+
14
class EvaluationContext:
2-
def __init__(self, targeting_key: str = None, attributes: dict = None):
5+
def __init__(
6+
self,
7+
targeting_key: typing.Optional[str] = None,
8+
attributes: typing.Optional[dict] = None,
9+
):
310
self.targeting_key = targeting_key
411
self.attributes = attributes or {}
512

open_feature/exception/exceptions.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class OpenFeatureError(Exception):
1010
"""
1111

1212
def __init__(
13-
self, error_message: typing.Optional[str] = None, error_code: ErrorCode = None
13+
self, error_code: ErrorCode, error_message: typing.Optional[str] = None
1414
):
1515
"""
1616
Constructor for the generic OpenFeatureError.
@@ -35,7 +35,7 @@ def __init__(self, error_message: typing.Optional[str] = None):
3535
@param error_message: an optional string message representing
3636
why the error has been raised
3737
"""
38-
super().__init__(error_message, ErrorCode.FLAG_NOT_FOUND)
38+
super().__init__(ErrorCode.FLAG_NOT_FOUND, error_message)
3939

4040

4141
class GeneralError(OpenFeatureError):
@@ -51,7 +51,7 @@ def __init__(self, error_message: typing.Optional[str] = None):
5151
@param error_message: an optional string message representing why the error
5252
has been raised
5353
"""
54-
super().__init__(error_message, ErrorCode.GENERAL)
54+
super().__init__(ErrorCode.GENERAL, error_message)
5555

5656

5757
class ParseError(OpenFeatureError):
@@ -67,7 +67,7 @@ def __init__(self, error_message: typing.Optional[str] = None):
6767
@param error_message: an optional string message representing why the
6868
error has been raised
6969
"""
70-
super().__init__(error_message, ErrorCode.PARSE_ERROR)
70+
super().__init__(ErrorCode.PARSE_ERROR, error_message)
7171

7272

7373
class TypeMismatchError(OpenFeatureError):
@@ -83,7 +83,7 @@ def __init__(self, error_message: typing.Optional[str] = None):
8383
@param error_message: an optional string message representing why the
8484
error has been raised
8585
"""
86-
super().__init__(error_message, ErrorCode.TYPE_MISMATCH)
86+
super().__init__(ErrorCode.TYPE_MISMATCH, error_message)
8787

8888

8989
class TargetingKeyMissingError(OpenFeatureError):
@@ -92,14 +92,14 @@ class TargetingKeyMissingError(OpenFeatureError):
9292
but one was not provided in the evaluation context.
9393
"""
9494

95-
def __init__(self, error_message: str = None):
95+
def __init__(self, error_message: typing.Optional[str] = None):
9696
"""
9797
Constructor for the TargetingKeyMissingError. The error code for this type of
9898
exception is ErrorCode.TARGETING_KEY_MISSING.
9999
@param error_message: a string message representing why the error has been
100100
raised
101101
"""
102-
super().__init__(error_message, ErrorCode.TARGETING_KEY_MISSING)
102+
super().__init__(ErrorCode.TARGETING_KEY_MISSING, error_message)
103103

104104

105105
class InvalidContextError(OpenFeatureError):
@@ -108,11 +108,11 @@ class InvalidContextError(OpenFeatureError):
108108
requirements.
109109
"""
110110

111-
def __init__(self, error_message: str = None):
111+
def __init__(self, error_message: typing.Optional[str]):
112112
"""
113113
Constructor for the InvalidContextError. The error code for this type of
114114
exception is ErrorCode.INVALID_CONTEXT.
115115
@param error_message: a string message representing why the error has been
116116
raised
117117
"""
118-
super().__init__(error_message, ErrorCode.INVALID_CONTEXT)
118+
super().__init__(ErrorCode.INVALID_CONTEXT, error_message)

open_feature/flag_evaluation/flag_evaluation_details.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
from open_feature.exception.error_code import ErrorCode
55
from open_feature.flag_evaluation.reason import Reason
66

7+
T = typing.TypeVar("T", covariant=True)
8+
79

810
@dataclass
9-
class FlagEvaluationDetails:
11+
class FlagEvaluationDetails(typing.Generic[T]):
1012
flag_key: str
11-
value: typing.Any
13+
value: T
1214
variant: typing.Optional[str] = None
1315
reason: typing.Optional[Reason] = None
1416
error_code: typing.Optional[ErrorCode] = None

open_feature/open_feature_api.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
from open_feature.open_feature_client import OpenFeatureClient
55
from open_feature.provider.provider import AbstractProvider
66

7-
_provider = None
7+
_provider: typing.Optional[AbstractProvider] = None
88

99

10-
def get_client(name: str = None, version: str = None) -> OpenFeatureClient:
10+
def get_client(
11+
name: typing.Optional[str] = None, version: typing.Optional[str] = None
12+
) -> OpenFeatureClient:
1113
if _provider is None:
1214
raise GeneralError(
1315
error_message="Provider not set. Call set_provider before using get_client"

open_feature/open_feature_client.py

+47-36
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
OpenFeatureError,
99
TypeMismatchError,
1010
)
11-
from open_feature.exception.exceptions import GeneralError
12-
from open_feature.flag_evaluation.error_code import ErrorCode
1311
from open_feature.flag_evaluation.flag_evaluation_details import FlagEvaluationDetails
1412
from open_feature.flag_evaluation.flag_evaluation_options import FlagEvaluationOptions
1513
from open_feature.flag_evaluation.flag_type import FlagType
@@ -26,20 +24,34 @@
2624
from open_feature.provider.no_op_provider import NoOpProvider
2725
from open_feature.provider.provider import AbstractProvider
2826

29-
NUMERIC_TYPES = [FlagType.FLOAT, FlagType.INTEGER]
30-
GetDetailsCallable = typing.Callable[
31-
[str, typing.Any, typing.Optional[EvaluationContext]], FlagEvaluationDetails
27+
28+
GetDetailCallable = typing.Union[
29+
typing.Callable[
30+
[str, bool, typing.Optional[EvaluationContext]], FlagEvaluationDetails[bool]
31+
],
32+
typing.Callable[
33+
[str, int, typing.Optional[EvaluationContext]], FlagEvaluationDetails[int]
34+
],
35+
typing.Callable[
36+
[str, float, typing.Optional[EvaluationContext]], FlagEvaluationDetails[float]
37+
],
38+
typing.Callable[
39+
[str, str, typing.Optional[EvaluationContext]], FlagEvaluationDetails[str]
40+
],
41+
typing.Callable[
42+
[str, dict, typing.Optional[EvaluationContext]], FlagEvaluationDetails[dict]
43+
],
3244
]
3345

3446

3547
class OpenFeatureClient:
3648
def __init__(
3749
self,
38-
name: str,
39-
version: str,
40-
context: EvaluationContext = None,
41-
hooks: typing.List[Hook] = None,
42-
provider: AbstractProvider = None,
50+
name: typing.Optional[str],
51+
version: typing.Optional[str],
52+
provider: AbstractProvider,
53+
context: typing.Optional[EvaluationContext] = None,
54+
hooks: typing.Optional[typing.List[Hook]] = None,
4355
):
4456
self.name = name
4557
self.version = version
@@ -54,8 +66,8 @@ def get_boolean_value(
5466
self,
5567
flag_key: str,
5668
default_value: bool,
57-
evaluation_context: EvaluationContext = None,
58-
flag_evaluation_options: FlagEvaluationOptions = None,
69+
evaluation_context: typing.Optional[EvaluationContext] = None,
70+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
5971
) -> bool:
6072
return self.evaluate_flag_details(
6173
FlagType.BOOLEAN,
@@ -69,8 +81,8 @@ def get_boolean_details(
6981
self,
7082
flag_key: str,
7183
default_value: bool,
72-
evaluation_context: EvaluationContext = None,
73-
flag_evaluation_options: FlagEvaluationOptions = None,
84+
evaluation_context: typing.Optional[EvaluationContext] = None,
85+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
7486
) -> FlagEvaluationDetails:
7587
return self.evaluate_flag_details(
7688
FlagType.BOOLEAN,
@@ -84,8 +96,8 @@ def get_string_value(
8496
self,
8597
flag_key: str,
8698
default_value: str,
87-
evaluation_context: EvaluationContext = None,
88-
flag_evaluation_options: FlagEvaluationOptions = None,
99+
evaluation_context: typing.Optional[EvaluationContext] = None,
100+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
89101
) -> str:
90102
return self.evaluate_flag_details(
91103
FlagType.STRING,
@@ -99,8 +111,8 @@ def get_string_details(
99111
self,
100112
flag_key: str,
101113
default_value: str,
102-
evaluation_context: EvaluationContext = None,
103-
flag_evaluation_options: FlagEvaluationOptions = None,
114+
evaluation_context: typing.Optional[EvaluationContext] = None,
115+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
104116
) -> FlagEvaluationDetails:
105117
return self.evaluate_flag_details(
106118
FlagType.STRING,
@@ -114,8 +126,8 @@ def get_integer_value(
114126
self,
115127
flag_key: str,
116128
default_value: int,
117-
evaluation_context: EvaluationContext = None,
118-
flag_evaluation_options: FlagEvaluationOptions = None,
129+
evaluation_context: typing.Optional[EvaluationContext] = None,
130+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
119131
) -> int:
120132
return self.get_integer_details(
121133
flag_key,
@@ -128,8 +140,8 @@ def get_integer_details(
128140
self,
129141
flag_key: str,
130142
default_value: int,
131-
evaluation_context: EvaluationContext = None,
132-
flag_evaluation_options: FlagEvaluationOptions = None,
143+
evaluation_context: typing.Optional[EvaluationContext] = None,
144+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
133145
) -> FlagEvaluationDetails:
134146
return self.evaluate_flag_details(
135147
FlagType.INTEGER,
@@ -143,8 +155,8 @@ def get_float_value(
143155
self,
144156
flag_key: str,
145157
default_value: float,
146-
evaluation_context: EvaluationContext = None,
147-
flag_evaluation_options: FlagEvaluationOptions = None,
158+
evaluation_context: typing.Optional[EvaluationContext] = None,
159+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
148160
) -> float:
149161
return self.get_float_details(
150162
flag_key,
@@ -157,8 +169,8 @@ def get_float_details(
157169
self,
158170
flag_key: str,
159171
default_value: float,
160-
evaluation_context: EvaluationContext = None,
161-
flag_evaluation_options: FlagEvaluationOptions = None,
172+
evaluation_context: typing.Optional[EvaluationContext] = None,
173+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
162174
) -> FlagEvaluationDetails:
163175
return self.evaluate_flag_details(
164176
FlagType.FLOAT,
@@ -172,8 +184,8 @@ def get_object_value(
172184
self,
173185
flag_key: str,
174186
default_value: dict,
175-
evaluation_context: EvaluationContext = None,
176-
flag_evaluation_options: FlagEvaluationOptions = None,
187+
evaluation_context: typing.Optional[EvaluationContext] = None,
188+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
177189
) -> dict:
178190
return self.evaluate_flag_details(
179191
FlagType.OBJECT,
@@ -187,8 +199,8 @@ def get_object_details(
187199
self,
188200
flag_key: str,
189201
default_value: dict,
190-
evaluation_context: EvaluationContext = None,
191-
flag_evaluation_options: FlagEvaluationOptions = None,
202+
evaluation_context: typing.Optional[EvaluationContext] = None,
203+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
192204
) -> FlagEvaluationDetails:
193205
return self.evaluate_flag_details(
194206
FlagType.OBJECT,
@@ -203,8 +215,8 @@ def evaluate_flag_details(
203215
flag_type: FlagType,
204216
flag_key: str,
205217
default_value: typing.Any,
206-
evaluation_context: EvaluationContext = None,
207-
flag_evaluation_options: FlagEvaluationOptions = None,
218+
evaluation_context: typing.Optional[EvaluationContext] = None,
219+
flag_evaluation_options: typing.Optional[FlagEvaluationOptions] = None,
208220
) -> FlagEvaluationDetails:
209221
"""
210222
Evaluate the flag requested by the user from the clients provider.
@@ -307,7 +319,7 @@ def _create_provider_evaluation(
307319
flag_type: FlagType,
308320
flag_key: str,
309321
default_value: typing.Any,
310-
evaluation_context: EvaluationContext = None,
322+
evaluation_context: typing.Optional[EvaluationContext] = None,
311323
) -> FlagEvaluationDetails:
312324
"""
313325
Encapsulated method to create a FlagEvaluationDetail from a specific provider.
@@ -329,16 +341,15 @@ def _create_provider_evaluation(
329341
logging.info("No provider configured, using no-op provider.")
330342
self.provider = NoOpProvider()
331343

332-
get_details_callable = {
344+
get_details_callables: typing.Mapping[FlagType, GetDetailCallable] = {
333345
FlagType.BOOLEAN: self.provider.resolve_boolean_details,
334346
FlagType.INTEGER: self.provider.resolve_integer_details,
335347
FlagType.FLOAT: self.provider.resolve_float_details,
336348
FlagType.OBJECT: self.provider.resolve_object_details,
337349
FlagType.STRING: self.provider.resolve_string_details,
338-
}.get(flag_type)
350+
}
339351

340352
get_details_callable = get_details_callables.get(flag_type)
341-
342353
if not get_details_callable:
343354
raise GeneralError(error_message="Unknown flag type")
344355

open_feature/provider/no_op_provider.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ def resolve_boolean_details(
2222
self,
2323
flag_key: str,
2424
default_value: bool,
25-
evaluation_context: Optional[EvaluationContext] = None,
26-
):
25+
evaluation_context: typing.Optional[EvaluationContext] = None,
26+
) -> FlagEvaluationDetails[bool]:
2727
return FlagEvaluationDetails(
2828
flag_key=flag_key,
2929
value=default_value,
@@ -35,8 +35,8 @@ def resolve_string_details(
3535
self,
3636
flag_key: str,
3737
default_value: str,
38-
evaluation_context: Optional[EvaluationContext] = None,
39-
):
38+
evaluation_context: typing.Optional[EvaluationContext] = None,
39+
) -> FlagEvaluationDetails[str]:
4040
return FlagEvaluationDetails(
4141
flag_key=flag_key,
4242
value=default_value,
@@ -48,8 +48,8 @@ def resolve_integer_details(
4848
self,
4949
flag_key: str,
5050
default_value: int,
51-
evaluation_context: EvaluationContext = None,
52-
):
51+
evaluation_context: typing.Optional[EvaluationContext] = None,
52+
) -> FlagEvaluationDetails[int]:
5353
return FlagEvaluationDetails(
5454
flag_key=flag_key,
5555
value=default_value,
@@ -61,8 +61,8 @@ def resolve_float_details(
6161
self,
6262
flag_key: str,
6363
default_value: float,
64-
evaluation_context: EvaluationContext = None,
65-
):
64+
evaluation_context: typing.Optional[EvaluationContext] = None,
65+
) -> FlagEvaluationDetails[float]:
6666
return FlagEvaluationDetails(
6767
flag_key=flag_key,
6868
value=default_value,
@@ -74,8 +74,8 @@ def resolve_object_details(
7474
self,
7575
flag_key: str,
7676
default_value: dict,
77-
evaluation_context: Optional[EvaluationContext] = None,
78-
):
77+
evaluation_context: typing.Optional[EvaluationContext] = None,
78+
) -> FlagEvaluationDetails[dict]:
7979
return FlagEvaluationDetails(
8080
flag_key=flag_key,
8181
value=default_value,

0 commit comments

Comments
 (0)