@@ -19,6 +19,8 @@ class Client(object): # pylint: disable=too-many-instance-attributes
1919
2020 _METRIC_GET_TREATMENT = 'sdk.getTreatment'
2121 _METRIC_GET_TREATMENTS = 'sdk.getTreatments'
22+ _METRIC_GET_TREATMENT_WITH_CONFIG = 'sdk.getTreatmentWithConfig'
23+ _METRIC_GET_TREATMENTS_WITH_CONFIG = 'sdk.getTreatmentsWithConfig'
2224
2325 def __init__ (self , factory , labels_enabled = True , impression_listener = None ):
2426 """
@@ -103,39 +105,25 @@ def _evaluate_if_ready(self, matching_key, bucketing_key, feature, attributes=No
103105 attributes
104106 )
105107
106- def get_treatment_with_config (self , key , feature , attributes = None ):
107- """
108- Get the treatment and config for a feature and key, with optional dictionary of attributes.
109-
110- This method never raises an exception. If there's a problem, the appropriate log message
111- will be generated and the method will return the CONTROL treatment.
112-
113- :param key: The key for which to get the treatment
114- :type key: str
115- :param feature: The name of the feature for which to get the treatment
116- :type feature: str
117- :param attributes: An optional dictionary of attributes
118- :type attributes: dict
119- :return: The treatment for the key and feature
120- :rtype: tuple(str, str)
121- """
108+ def _make_evaluation (self , key , feature , attributes , method_name , metric_name ):
122109 try :
123110 if self .destroyed :
124111 self ._logger .error ("Client has already been destroyed - no calls possible" )
125112 return CONTROL , None
126113
127114 start = int (round (time .time () * 1000 ))
128115
129- matching_key , bucketing_key = input_validator .validate_key (key )
116+ matching_key , bucketing_key = input_validator .validate_key (key , method_name )
130117 feature = input_validator .validate_feature_name (
131118 feature ,
132119 self .ready ,
133- self ._factory ._get_storage ('splits' ) # pylint: disable=protected-access
120+ self ._factory ._get_storage ('splits' ), # pylint: disable=protected-access
121+ method_name
134122 )
135123
136124 if (matching_key is None and bucketing_key is None ) \
137125 or feature is None \
138- or not input_validator .validate_attributes (attributes ):
126+ or not input_validator .validate_attributes (attributes , method_name ):
139127 return CONTROL , None
140128
141129 result = self ._evaluate_if_ready (matching_key , bucketing_key , feature , attributes )
@@ -150,7 +138,7 @@ def get_treatment_with_config(self, key, feature, attributes=None):
150138 start
151139 )
152140
153- self ._record_stats (impression , start , self . _METRIC_GET_TREATMENT )
141+ self ._record_stats ([ impression ] , start , metric_name )
154142 self ._send_impression_to_listener (impression , attributes )
155143 return result ['treatment' ], result ['configurations' ]
156144 except Exception : # pylint: disable=broad-except
@@ -166,80 +154,29 @@ def get_treatment_with_config(self, key, feature, attributes=None):
166154 bucketing_key ,
167155 start
168156 )
169- self ._record_stats (impression , start , self . _METRIC_GET_TREATMENT )
157+ self ._record_stats ([ impression ] , start , metric_name )
170158 self ._send_impression_to_listener (impression , attributes )
171159 except Exception : # pylint: disable=broad-except
172160 self ._logger .error ('Error reporting impression into get_treatment exception block' )
173161 self ._logger .debug ('Error: ' , exc_info = True )
174162 return CONTROL , None
175163
176- def get_treatment (self , key , feature , attributes = None ):
177- """
178- Get the treatment for a feature and key, with an optional dictionary of attributes.
179-
180- This method never raises an exception. If there's a problem, the appropriate log message
181- will be generated and the method will return the CONTROL treatment.
182-
183- :param key: The key for which to get the treatment
184- :type key: str
185- :param feature: The name of the feature for which to get the treatment
186- :type feature: str
187- :param attributes: An optional dictionary of attributes
188- :type attributes: dict
189- :return: The treatment for the key and feature
190- :rtype: str
191- """
192- treatment , _ = self .get_treatment_with_config (key , feature , attributes )
193- return treatment
194-
195- def _evaluate_features_if_ready (self , matching_key , bucketing_key , features , attributes = None ):
196- if not self .ready :
197- return {
198- feature : {
199- 'treatment' : CONTROL ,
200- 'configurations' : None ,
201- 'impression' : {'label' : Label .NOT_READY , 'change_number' : None }
202- }
203- for feature in features
204- }
205-
206- return self ._evaluator .evaluate_features (
207- features ,
208- matching_key ,
209- bucketing_key ,
210- attributes
211- )
212-
213- def get_treatments_with_config (self , key , features , attributes = None ):
214- """
215- Evaluate multiple features and return a dict with feature -> (treatment, config).
216-
217- Get the treatments for a list of features considering a key, with an optional dictionary of
218- attributes. This method never raises an exception. If there's a problem, the appropriate
219- log message will be generated and the method will return the CONTROL treatment.
220- :param key: The key for which to get the treatment
221- :type key: str
222- :param features: Array of the names of the features for which to get the treatment
223- :type feature: list
224- :param attributes: An optional dictionary of attributes
225- :type attributes: dict
226- :return: Dictionary with the result of all the features provided
227- :rtype: dict
228- """
164+ def _make_evaluations (self , key , features , attributes , method_name , metric_name ):
229165 if self .destroyed :
230166 self ._logger .error ("Client has already been destroyed - no calls possible" )
231- return input_validator .generate_control_treatments (features )
167+ return input_validator .generate_control_treatments (features , method_name )
232168
233169 start = int (round (time .time () * 1000 ))
234170
235- matching_key , bucketing_key = input_validator .validate_key (key )
171+ matching_key , bucketing_key = input_validator .validate_key (key , method_name )
236172 if matching_key is None and bucketing_key is None :
237- return input_validator .generate_control_treatments (features )
173+ return input_validator .generate_control_treatments (features , method_name )
238174
239- if input_validator .validate_attributes (attributes ) is False :
240- return input_validator .generate_control_treatments (features )
175+ if input_validator .validate_attributes (attributes , method_name ) is False :
176+ return input_validator .generate_control_treatments (features , method_name )
241177
242178 features , missing = input_validator .validate_features_get_treatments (
179+ method_name ,
243180 features ,
244181 self .ready ,
245182 self ._factory ._get_storage ('splits' ) # pylint: disable=protected-access
@@ -269,8 +206,8 @@ def get_treatments_with_config(self, key, features, attributes=None):
269206 treatments [feature ] = (result ['treatment' ], result ['configurations' ])
270207
271208 except Exception : # pylint: disable=broad-except
272- self ._logger .error ('get_treatments : An exception occured when evaluating '
273- 'feature ' + feature + ' returning CONTROL.' )
209+ self ._logger .error ('%s : An exception occured when evaluating '
210+ 'feature %s returning CONTROL.' % ( method_name , feature ) )
274211 treatments [feature ] = CONTROL , None
275212 self ._logger .debug ('Error: ' , exc_info = True )
276213 continue
@@ -282,15 +219,91 @@ def get_treatments_with_config(self, key, features, attributes=None):
282219 for impression in bulk_impressions :
283220 self ._send_impression_to_listener (impression , attributes )
284221 except Exception : # pylint: disable=broad-except
285- self ._logger .error ('get_treatments : An exception when trying to store '
286- 'impressions.' )
222+ self ._logger .error ('%s : An exception when trying to store '
223+ 'impressions.' % method_name )
287224 self ._logger .debug ('Error: ' , exc_info = True )
288225
289226 return treatments
290227 except Exception : # pylint: disable=broad-except
291228 self ._logger .error ('Error getting treatment for features' )
292229 self ._logger .debug ('Error: ' , exc_info = True )
293- return input_validator .generate_control_treatments (list (features ))
230+ return input_validator .generate_control_treatments (list (features ), method_name )
231+
232+ def _evaluate_features_if_ready (self , matching_key , bucketing_key , features , attributes = None ):
233+ if not self .ready :
234+ return {
235+ feature : {
236+ 'treatment' : CONTROL ,
237+ 'configurations' : None ,
238+ 'impression' : {'label' : Label .NOT_READY , 'change_number' : None }
239+ }
240+ for feature in features
241+ }
242+
243+ return self ._evaluator .evaluate_features (
244+ features ,
245+ matching_key ,
246+ bucketing_key ,
247+ attributes
248+ )
249+
250+ def get_treatment_with_config (self , key , feature , attributes = None ):
251+ """
252+ Get the treatment and config for a feature and key, with optional dictionary of attributes.
253+
254+ This method never raises an exception. If there's a problem, the appropriate log message
255+ will be generated and the method will return the CONTROL treatment.
256+
257+ :param key: The key for which to get the treatment
258+ :type key: str
259+ :param feature: The name of the feature for which to get the treatment
260+ :type feature: str
261+ :param attributes: An optional dictionary of attributes
262+ :type attributes: dict
263+ :return: The treatment for the key and feature
264+ :rtype: tuple(str, str)
265+ """
266+ return self ._make_evaluation (key , feature , attributes , 'get_treatment_with_config' ,
267+ self ._METRIC_GET_TREATMENT_WITH_CONFIG )
268+
269+ def get_treatment (self , key , feature , attributes = None ):
270+ """
271+ Get the treatment for a feature and key, with an optional dictionary of attributes.
272+
273+ This method never raises an exception. If there's a problem, the appropriate log message
274+ will be generated and the method will return the CONTROL treatment.
275+
276+ :param key: The key for which to get the treatment
277+ :type key: str
278+ :param feature: The name of the feature for which to get the treatment
279+ :type feature: str
280+ :param attributes: An optional dictionary of attributes
281+ :type attributes: dict
282+ :return: The treatment for the key and feature
283+ :rtype: str
284+ """
285+ treatment , _ = self ._make_evaluation (key , feature , attributes , 'get_treatment' ,
286+ self ._METRIC_GET_TREATMENT )
287+ return treatment
288+
289+ def get_treatments_with_config (self , key , features , attributes = None ):
290+ """
291+ Evaluate multiple features and return a dict with feature -> (treatment, config).
292+
293+ Get the treatments for a list of features considering a key, with an optional dictionary of
294+ attributes. This method never raises an exception. If there's a problem, the appropriate
295+ log message will be generated and the method will return the CONTROL treatment.
296+ :param key: The key for which to get the treatment
297+ :type key: str
298+ :param features: Array of the names of the features for which to get the treatment
299+ :type feature: list
300+ :param attributes: An optional dictionary of attributes
301+ :type attributes: dict
302+ :return: Dictionary with the result of all the features provided
303+ :rtype: dict
304+ """
305+ return self ._make_evaluations (key , features , attributes , 'get_treatments_with_config' ,
306+ self ._METRIC_GET_TREATMENTS_WITH_CONFIG )
294307
295308 def get_treatments (self , key , features , attributes = None ):
296309 """
@@ -308,7 +321,8 @@ def get_treatments(self, key, features, attributes=None):
308321 :return: Dictionary with the result of all the features provided
309322 :rtype: dict
310323 """
311- with_config = self .get_treatments_with_config (key , features , attributes )
324+ with_config = self ._make_evaluations (key , features , attributes , 'get_treatments' ,
325+ self ._METRIC_GET_TREATMENTS )
312326 return {feature : result [0 ] for (feature , result ) in six .iteritems (with_config )}
313327
314328 def _build_impression ( # pylint: disable=too-many-arguments
@@ -346,10 +360,7 @@ def _record_stats(self, impressions, start, operation):
346360 """
347361 try :
348362 end = int (round (time .time () * 1000 ))
349- if operation == self ._METRIC_GET_TREATMENT :
350- self ._impressions_storage .put ([impressions ])
351- else :
352- self ._impressions_storage .put (impressions )
363+ self ._impressions_storage .put (impressions )
353364 self ._telemetry_storage .inc_latency (operation , get_latency_bucket_index (end - start ))
354365 except Exception : # pylint: disable=broad-except
355366 self ._logger .error ('Error recording impressions and metrics' )
0 commit comments