Skip to content

Commit 0c92722

Browse files
fix: typechecks added
1 parent e6e442a commit 0c92722

File tree

3 files changed

+34
-22
lines changed

3 files changed

+34
-22
lines changed

optimizely/decision_service.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Decision(NamedTuple):
3535
None if no experiment/variation was selected."""
3636
experiment: Optional[entities.Experiment]
3737
variation: Optional[entities.Variation]
38-
source: str
38+
source: Optional[str]
3939

4040

4141
class DecisionService:
@@ -247,7 +247,7 @@ def get_variation(
247247
project_config: ProjectConfig,
248248
experiment: entities.Experiment,
249249
user_context: OptimizelyUserContext,
250-
user_profile_tracker: UserProfileTracker,
250+
user_profile_tracker: Optional[UserProfileTracker],
251251
reasons: list[str] = [],
252252
options: Optional[Sequence[str]] = None
253253
) -> tuple[Optional[entities.Variation], list[str]]:
@@ -341,7 +341,8 @@ def get_variation(
341341
if user_profile_tracker is not None and not ignore_user_profile:
342342
try:
343343
user_profile_tracker.update_user_profile(experiment, variation)
344-
self.user_profile_service.save(user_profile_tracker.get_user_profile().__dict__)
344+
if self.user_profile_service is not None:
345+
self.user_profile_service.save(user_profile_tracker.get_user_profile().__dict__)
345346
except:
346347
self.logger.exception(f'Unable to save user profile for user "{user_id}".')
347348
return variation, decide_reasons
@@ -561,15 +562,15 @@ def get_variations_for_feature_list(
561562
Returns:
562563
List of Decision namedtuple consisting of experiment and variation for the user.
563564
"""
564-
decide_reasons = []
565+
decide_reasons: list[str] = []
565566

566567
if options:
567568
ignore_ups = OptimizelyDecideOption.IGNORE_USER_PROFILE_SERVICE in options
568569
else:
569570
ignore_ups = False
570571

571572

572-
user_profile_tracker: UserProfileTracker = None
573+
user_profile_tracker: Optional[UserProfileTracker] = None
573574
if self.user_profile_service is not None and not ignore_ups:
574575
user_profile_tracker = UserProfileTracker(user_context.user_id, self.user_profile_service, self.logger)
575576
user_profile_tracker.load_user_profile(decide_reasons, None)
@@ -611,16 +612,16 @@ def get_variations_for_feature_list(
611612
# Only process rollout if no experiment decision was found
612613
if not experiment_decision_found:
613614
rollout_decision, rollout_reasons = self.get_variation_for_rollout(project_config, feature, user_context)
614-
feature_reasons.append(rollout_reasons)
615-
615+
if rollout_reasons:
616+
feature_reasons.extend(rollout_reasons)
616617
if rollout_decision:
617618
self.logger.debug(f'User "{user_context.user_id}" bucketed into rollout for feature "{feature.key}".')
618619
else:
619620
self.logger.debug(f'User "{user_context.user_id}" not bucketed into any rollout for feature "{feature.key}".')
620621

621622
decisions.append((rollout_decision, feature_reasons))
622623

623-
if self.user_profile_service is not None and ignore_ups is False:
624+
if self.user_profile_service is not None and user_profile_tracker is not None and ignore_ups is False:
624625
user_profile_tracker.save_user_profile()
625626

626627
return decisions

optimizely/user_profile.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
# limitations under the License.
1313

1414
from __future__ import annotations
15-
from typing import Any, Optional
15+
from typing import Any, Optional, Union
1616
from sys import version_info
1717
from . import logger as _logging
1818
from . import decision_service
19-
19+
from .helpers import enums
2020
if version_info < (3, 8):
2121
from typing_extensions import Final
2222
else:
@@ -46,7 +46,7 @@ class UserProfile:
4646
def __init__(
4747
self,
4848
user_id: str,
49-
experiment_bucket_map: Optional[dict[str, Decision]] = None,
49+
experiment_bucket_map: Optional[dict[str, Union[Decision, dict[str, str]]]] = None,
5050
**kwargs: Any
5151
):
5252
self.user_id = user_id
@@ -64,8 +64,14 @@ def get_variation_for_experiment(self, experiment_id: str) -> Optional[str]:
6464
Returns:
6565
Variation ID corresponding to the experiment. None if no decision available.
6666
"""
67+
experiment_data = self.experiment_bucket_map.get(experiment_id)
68+
69+
if isinstance(experiment_data, decision_service.Decision):
70+
return experiment_data.variation.id if experiment_data.variation is not None else None
71+
elif isinstance(experiment_data, dict):
72+
return experiment_data.get(self.VARIATION_ID_KEY)
6773

68-
return self.experiment_bucket_map.get(experiment_id, {self.VARIATION_ID_KEY: None}).get(self.VARIATION_ID_KEY)
74+
return None
6975

7076
def save_variation_for_experiment(self, experiment_id: str, variation_id: str) -> None:
7177
""" Helper method to save new experiment/variation as part of the user's profile.
@@ -74,7 +80,6 @@ def save_variation_for_experiment(self, experiment_id: str, variation_id: str) -
7480
experiment_id: ID for experiment for which the decision is to be stored.
7581
variation_id: ID for variation that the user saw.
7682
"""
77-
7883
self.experiment_bucket_map.update({experiment_id: {self.VARIATION_ID_KEY: variation_id}})
7984

8085

@@ -107,16 +112,18 @@ def __init__(self, user_id: str, user_profile_service: UserProfileService, logge
107112
self.user_profile_service = user_profile_service
108113
self.logger = _logging.adapt_logger(logger or _logging.NoOpLogger())
109114
self.profile_updated = False
110-
self.user_profile = None
115+
self.user_profile = UserProfile(user_id, {})
111116

112117
def get_user_profile(self):
113118
return self.user_profile
114119

115120
def load_user_profile(self, reasons: Optional[list[str]]=[], error_handler: Optional[BaseErrorHandler]=None):
121+
reasons = reasons if reasons else []
116122
try:
117123
user_profile = self.user_profile_service.lookup(self.user_id)
118124
if user_profile is None:
119-
message = reasons.append("Unable to get a user profile from the UserProfileService.")
125+
message = "Unable to get a user profile from the UserProfileService."
126+
reasons.append(message)
120127
self.logger.info(message)
121128
else:
122129
if 'user_id' in user_profile and 'experiment_bucket_map' in user_profile:
@@ -130,7 +137,8 @@ def load_user_profile(self, reasons: Optional[list[str]]=[], error_handler: Opti
130137
message = f"User profile is missing keys: {', '.join(missing_keys)}"
131138
reasons.append(message)
132139
except Exception as exception:
133-
message = reasons.append(str(exception))
140+
message = str(exception)
141+
reasons.append(message)
134142
self.logger.exception(f'Unable to retrieve user profile for user "{self.user_id}"as lookup failed.')
135143
# Todo: add error handler
136144
# error_handler.handle_error()
@@ -139,10 +147,14 @@ def load_user_profile(self, reasons: Optional[list[str]]=[], error_handler: Opti
139147
self.user_profile = UserProfile(self.user_id, {})
140148

141149
def update_user_profile(self, experiment: Experiment, variation: Variation):
142-
decision:Decision = None
143150
if experiment.id in self.user_profile.experiment_bucket_map:
144151
decision = self.user_profile.experiment_bucket_map[experiment.id]
145-
decision.variation = variation
152+
if isinstance(decision, decision_service.Decision):
153+
decision = decision_service.Decision(
154+
experiment=decision.experiment,
155+
variation=variation,
156+
source=decision.source
157+
)
146158
else:
147159
decision = decision_service.Decision(experiment=None, variation=variation, source=None)
148160

@@ -154,9 +166,8 @@ def update_user_profile(self, experiment: Experiment, variation: Variation):
154166
def save_user_profile(self, error_handler: Optional[BaseErrorHandler] = None):
155167
if not self.profile_updated:
156168
return
157-
158169
try:
159-
self.user_profile_service.save(self.user_profile)
170+
self.user_profile_service.save(self.user_profile.__dict__)
160171
self.logger.info(f'Saved user profile of user "{self.user_profile.user_id}".')
161172
except Exception as exception:
162173
self.logger.warning(f'Failed to save user profile of user "{self.user_profile.user_id}".')

tests/test_decision_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,7 +1495,7 @@ def test_get_variation_for_feature__returns_none_for_user_not_in_experiment(self
14951495
self.project_config.get_experiment_from_key("test_experiment"),
14961496
user,
14971497
None,
1498-
[[]],
1498+
[],
14991499
None
15001500
)
15011501

@@ -1523,7 +1523,7 @@ def test_get_variation_for_feature__returns_none_for_user_in_group_experiment_no
15231523
)
15241524

15251525
mock_decision.assert_called_once_with(
1526-
self.project_config, self.project_config.get_experiment_from_id("32222"), user, None, [[]], False
1526+
self.project_config, self.project_config.get_experiment_from_id("32222"), user, None, [], False
15271527
)
15281528

15291529
def test_get_variation_for_feature__returns_variation_for_feature_in_mutex_group_bucket_less_than_2500(

0 commit comments

Comments
 (0)