Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit 4ff7636

Browse files
add try except to all public client methods (FF-947) (#26)
* add try except to all public client methods (FF-947) * test all methods * value error in all public methods
1 parent 42830db commit 4ff7636

File tree

4 files changed

+146
-48
lines changed

4 files changed

+146
-48
lines changed

eppo_client/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def init(config: Config) -> EppoClient:
3838
http_client=http_client, config_store=config_store
3939
)
4040
assignment_logger = config.assignment_logger
41+
is_graceful_mode = config.is_graceful_mode
4142
global __client
4243
global __lock
4344
try:
@@ -48,6 +49,7 @@ def init(config: Config) -> EppoClient:
4849
__client = EppoClient(
4950
config_requestor=config_requestor,
5051
assignment_logger=assignment_logger,
52+
is_graceful_mode=is_graceful_mode,
5153
)
5254
return __client
5355
finally:

eppo_client/client.py

Lines changed: 104 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ def __init__(
2525
self,
2626
config_requestor: ExperimentConfigurationRequestor,
2727
assignment_logger: AssignmentLogger,
28+
is_graceful_mode: bool = True,
2829
):
2930
self.__config_requestor = config_requestor
3031
self.__assignment_logger = assignment_logger
32+
self.__is_graceful_mode = is_graceful_mode
3133
self.__poller = Poller(
3234
interval_millis=POLL_INTERVAL_MILLIS,
3335
jitter_millis=POLL_JITTER_MILLIS,
@@ -38,77 +40,131 @@ def __init__(
3840
def get_string_assignment(
3941
self, subject_key: str, flag_key: str, subject_attributes=dict()
4042
) -> Optional[str]:
41-
assigned_variation = self.get_assignment_variation(
42-
subject_key, flag_key, subject_attributes, VariationType.STRING
43-
)
44-
return (
45-
assigned_variation.typed_value
46-
if assigned_variation is not None
47-
else assigned_variation
48-
)
43+
try:
44+
assigned_variation = self.get_assignment_variation(
45+
subject_key, flag_key, subject_attributes, VariationType.STRING
46+
)
47+
return (
48+
assigned_variation.typed_value
49+
if assigned_variation is not None
50+
else assigned_variation
51+
)
52+
except ValueError as e:
53+
# allow ValueError to bubble up as it is a validation error
54+
raise e
55+
except Exception as e:
56+
if self.__is_graceful_mode:
57+
logger.error("[Eppo SDK] Error getting assignment: " + str(e))
58+
return None
59+
raise e
4960

5061
def get_numeric_assignment(
5162
self, subject_key: str, flag_key: str, subject_attributes=dict()
5263
) -> Optional[Number]:
53-
assigned_variation = self.get_assignment_variation(
54-
subject_key, flag_key, subject_attributes, VariationType.NUMERIC
55-
)
56-
return (
57-
assigned_variation.typed_value
58-
if assigned_variation is not None
59-
else assigned_variation
60-
)
64+
try:
65+
assigned_variation = self.get_assignment_variation(
66+
subject_key, flag_key, subject_attributes, VariationType.NUMERIC
67+
)
68+
return (
69+
assigned_variation.typed_value
70+
if assigned_variation is not None
71+
else assigned_variation
72+
)
73+
except ValueError as e:
74+
# allow ValueError to bubble up as it is a validation error
75+
raise e
76+
except Exception as e:
77+
if self.__is_graceful_mode:
78+
logger.error("[Eppo SDK] Error getting assignment: " + str(e))
79+
return None
80+
raise e
6181

6282
def get_boolean_assignment(
6383
self, subject_key: str, flag_key: str, subject_attributes=dict()
6484
) -> Optional[bool]:
65-
assigned_variation = self.get_assignment_variation(
66-
subject_key, flag_key, subject_attributes, VariationType.BOOLEAN
67-
)
68-
return (
69-
assigned_variation.typed_value
70-
if assigned_variation is not None
71-
else assigned_variation
72-
)
85+
try:
86+
assigned_variation = self.get_assignment_variation(
87+
subject_key, flag_key, subject_attributes, VariationType.BOOLEAN
88+
)
89+
return (
90+
assigned_variation.typed_value
91+
if assigned_variation is not None
92+
else assigned_variation
93+
)
94+
except ValueError as e:
95+
# allow ValueError to bubble up as it is a validation error
96+
raise e
97+
except Exception as e:
98+
if self.__is_graceful_mode:
99+
logger.error("[Eppo SDK] Error getting assignment: " + str(e))
100+
return None
101+
raise e
73102

74103
def get_parsed_json_assignment(
75104
self, subject_key: str, flag_key: str, subject_attributes=dict()
76105
) -> Optional[Dict[Any, Any]]:
77-
assigned_variation = self.get_assignment_variation(
78-
subject_key, flag_key, subject_attributes, VariationType.JSON
79-
)
80-
return (
81-
assigned_variation.typed_value
82-
if assigned_variation is not None
83-
else assigned_variation
84-
)
106+
try:
107+
assigned_variation = self.get_assignment_variation(
108+
subject_key, flag_key, subject_attributes, VariationType.JSON
109+
)
110+
return (
111+
assigned_variation.typed_value
112+
if assigned_variation is not None
113+
else assigned_variation
114+
)
115+
except ValueError as e:
116+
# allow ValueError to bubble up as it is a validation error
117+
raise e
118+
except Exception as e:
119+
if self.__is_graceful_mode:
120+
logger.error("[Eppo SDK] Error getting assignment: " + str(e))
121+
return None
122+
raise e
85123

86124
def get_json_string_assignment(
87125
self, subject_key: str, flag_key: str, subject_attributes=dict()
88126
) -> Optional[str]:
89-
assigned_variation = self.get_assignment_variation(
90-
subject_key, flag_key, subject_attributes, VariationType.JSON
91-
)
92-
return (
93-
assigned_variation.value
94-
if assigned_variation is not None
95-
else assigned_variation
96-
)
127+
try:
128+
assigned_variation = self.get_assignment_variation(
129+
subject_key, flag_key, subject_attributes, VariationType.JSON
130+
)
131+
return (
132+
assigned_variation.value
133+
if assigned_variation is not None
134+
else assigned_variation
135+
)
136+
except ValueError as e:
137+
# allow ValueError to bubble up as it is a validation error
138+
raise e
139+
except Exception as e:
140+
if self.__is_graceful_mode:
141+
logger.error("[Eppo SDK] Error getting assignment: " + str(e))
142+
return None
143+
raise e
97144

98145
@deprecated(
99146
"get_assignment is deprecated in favor of the typed get_<type>_assignment methods"
100147
)
101148
def get_assignment(
102149
self, subject_key: str, flag_key: str, subject_attributes=dict()
103150
) -> Optional[str]:
104-
assigned_variation = self.get_assignment_variation(
105-
subject_key, flag_key, subject_attributes
106-
)
107-
return (
108-
assigned_variation.value
109-
if assigned_variation is not None
110-
else assigned_variation
111-
)
151+
try:
152+
assigned_variation = self.get_assignment_variation(
153+
subject_key, flag_key, subject_attributes
154+
)
155+
return (
156+
assigned_variation.value
157+
if assigned_variation is not None
158+
else assigned_variation
159+
)
160+
except ValueError as e:
161+
# allow ValueError to bubble up as it is a validation error
162+
raise e
163+
except Exception as e:
164+
if self.__is_graceful_mode:
165+
logger.error("[Eppo SDK] Error getting assignment: " + str(e))
166+
return None
167+
raise e
112168

113169
def get_assignment_variation(
114170
self,

eppo_client/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Config(SdkBaseModel):
88
api_key: str
99
base_url: str = "https://fscdn.eppo.cloud/api"
1010
assignment_logger: AssignmentLogger
11+
is_graceful_mode: bool = True
1112

1213
def _validate(self):
1314
validate_not_blank("api_key", self.api_key)

test/client_test.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,45 @@ def test_with_null_experiment_config(mock_config_requestor):
252252
assert client.get_assignment("user-1", "experiment-key-1") is None
253253

254254

255+
@patch("eppo_client.configuration_requestor.ExperimentConfigurationRequestor")
256+
@patch.object(EppoClient, "get_assignment_variation")
257+
def test_graceful_mode_on(mock_get_assignment_variation, mock_config_requestor):
258+
mock_get_assignment_variation.side_effect = Exception("This is a mock exception!")
259+
260+
client = EppoClient(
261+
config_requestor=mock_config_requestor,
262+
assignment_logger=AssignmentLogger(),
263+
is_graceful_mode=True,
264+
)
265+
266+
assert client.get_assignment("user-1", "experiment-key-1") is None
267+
assert client.get_boolean_assignment("user-1", "experiment-key-1") is None
268+
assert client.get_json_string_assignment("user-1", "experiment-key-1") is None
269+
assert client.get_numeric_assignment("user-1", "experiment-key-1") is None
270+
assert client.get_string_assignment("user-1", "experiment-key-1") is None
271+
assert client.get_parsed_json_assignment("user-1", "experiment-key-1") is None
272+
273+
274+
@patch("eppo_client.configuration_requestor.ExperimentConfigurationRequestor")
275+
@patch.object(EppoClient, "get_assignment_variation")
276+
def test_graceful_mode_off(mock_get_assignment_variation, mock_config_requestor):
277+
mock_get_assignment_variation.side_effect = Exception("This is a mock exception!")
278+
279+
client = EppoClient(
280+
config_requestor=mock_config_requestor,
281+
assignment_logger=AssignmentLogger(),
282+
is_graceful_mode=False,
283+
)
284+
285+
with pytest.raises(Exception):
286+
client.get_assignment("user-1", "experiment-key-1")
287+
client.get_boolean_assignment("user-1", "experiment-key-1")
288+
client.get_json_string_assignment("user-1", "experiment-key-1")
289+
client.get_numeric_assignment("user-1", "experiment-key-1")
290+
client.get_string_assignment("user-1", "experiment-key-1")
291+
client.get_parsed_json_assignment("user-1", "experiment-key-1")
292+
293+
255294
@pytest.mark.parametrize("test_case", test_data)
256295
def test_assign_subject_in_sample(test_case):
257296
print("---- Test case for {} Experiment".format(test_case["experiment"]))

0 commit comments

Comments
 (0)