Skip to content

Commit 789bac3

Browse files
committed
support MRs and retire owner
1 parent d197353 commit 789bac3

File tree

2 files changed

+87
-11
lines changed

2 files changed

+87
-11
lines changed

reddit_decider/__init__.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ class ExperimentConfig:
7373
bucket_val: str
7474
start_ts: int
7575
stop_ts: int
76-
owner: str
76+
owner: None = None
7777
emit_event: Optional[bool] = None
78+
measured: Optional[bool] = None
7879

7980

8081
class DeciderContext:
@@ -236,7 +237,6 @@ def _send_expose(self, event: str, exposure_fields: dict) -> None:
236237
bucket_val,
237238
start_ts,
238239
stop_ts,
239-
owner,
240240
) = event.split("::::")
241241
except ValueError:
242242
logger.warning(
@@ -251,7 +251,6 @@ def _send_expose(self, event: str, exposure_fields: dict) -> None:
251251
bucket_val=bucket_val,
252252
start_ts=self._cast_to_int(start_ts),
253253
stop_ts=self._cast_to_int(stop_ts),
254-
owner=owner,
255254
)
256255

257256
event_fields = {**event_fields, **{bucket_val: bucketing_value}}
@@ -279,7 +278,6 @@ def _send_expose_if_holdout(self, event: str, exposure_fields: dict) -> None:
279278
bucket_val,
280279
start_ts,
281280
stop_ts,
282-
owner,
283281
) = event.split("::::")
284282
except ValueError:
285283
logger.warning(
@@ -299,7 +297,6 @@ def _send_expose_if_holdout(self, event: str, exposure_fields: dict) -> None:
299297
bucket_val=bucket_val,
300298
start_ts=self._cast_to_int(start_ts),
301299
stop_ts=self._cast_to_int(stop_ts),
302-
owner=owner,
303300
)
304301

305302
event_fields = {**event_fields, **{bucket_val: bucketing_value}}
@@ -432,7 +429,6 @@ def expose(
432429
bucket_val=feature.bucket_val,
433430
start_ts=feature.start_ts,
434431
stop_ts=feature.stop_ts,
435-
owner=feature.owner,
436432
)
437433

438434
self._event_logger.log(
@@ -920,8 +916,8 @@ def get_experiment(self, experiment_name: str) -> Optional[ExperimentConfig]:
920916
bucket_val=feature.bucket_val,
921917
start_ts=feature.start_ts,
922918
stop_ts=feature.stop_ts,
923-
owner=feature.owner,
924919
emit_event=feature.emit_event,
920+
measured=feature.measured,
925921
)
926922

927923

tests/decider_tests.py

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ def assert_exposure_event_fields(
500500
cfg = self.exp_base_config[experiment_name]
501501
self.assertEqual(getattr(event_fields["experiment"], "id"), cfg["id"])
502502
self.assertEqual(getattr(event_fields["experiment"], "name"), cfg["name"])
503-
self.assertEqual(getattr(event_fields["experiment"], "owner"), cfg["owner"])
503+
self.assertEqual(getattr(event_fields["experiment"], "owner"), None)
504504
self.assertEqual(getattr(event_fields["experiment"], "version"), cfg["version"])
505505
self.assertEqual(getattr(event_fields["experiment"], "bucket_val"), bucket_val)
506506

@@ -520,7 +520,7 @@ def assert_minimal_exposure_event_fields(
520520
cfg = self.exp_base_config[experiment_name]
521521
self.assertEqual(getattr(event_fields["experiment"], "id"), cfg["id"])
522522
self.assertEqual(getattr(event_fields["experiment"], "name"), cfg["name"])
523-
self.assertEqual(getattr(event_fields["experiment"], "owner"), cfg["owner"])
523+
self.assertEqual(getattr(event_fields["experiment"], "owner"), None)
524524
self.assertEqual(getattr(event_fields["experiment"], "version"), cfg["version"])
525525
self.assertEqual(getattr(event_fields["experiment"], "bucket_val"), bucket_val)
526526

@@ -573,7 +573,7 @@ def test_none_returned_on_get_variant_call_with_bad_id(self):
573573
self.assertEqual(self.event_logger.log.call_count, 0)
574574

575575
assert any(
576-
"Partially loaded Decider: 1 features failed to load: {'test': 'Manifest parsing error: invalid type: string \"1\", expected u32'}"
576+
'Partially loaded Decider: 1 feature(s) failed to load: {"test": ParsingError(Error("invalid type: string \\"1\\", expected u32", line: 0, column: 0))'
577577
in x.getMessage()
578578
for x in captured.records
579579
)
@@ -1508,6 +1508,86 @@ def test_get_variant_with_disabled_exp(self):
15081508
# exposure assertions
15091509
self.assertEqual(self.event_logger.log.call_count, 0)
15101510

1511+
def test_range_variant_emit_event_override(self):
1512+
cfg = {
1513+
"feature_rollout_100": {
1514+
"id": 9110,
1515+
"name": "feature_rollout_100",
1516+
"enabled": True,
1517+
"version": "1",
1518+
"start_ts": 1522306800,
1519+
"stop_ts": 32533405261,
1520+
"owner": "[email protected]",
1521+
"type": "feature_rollout",
1522+
"emit_event": False,
1523+
"experiment": {
1524+
"variants": [
1525+
{
1526+
"name": "enabled",
1527+
"size": 1.0,
1528+
"range_end": 1.0,
1529+
"range_start": 0.0
1530+
}
1531+
],
1532+
"experiment_version": 1,
1533+
"shuffle_version": 0,
1534+
"bucket_val": "user_id",
1535+
"log_bucketing": False
1536+
}
1537+
},
1538+
"measured_rollout_100": {
1539+
"id": 9119,
1540+
"name": "measured_rollout_100",
1541+
"enabled": True,
1542+
"version": "1",
1543+
"start_ts": 1522306800,
1544+
"stop_ts": 32533405261,
1545+
"owner": "[email protected]",
1546+
"type": "range_variant",
1547+
"emit_event": False,
1548+
"measured": True,
1549+
"experiment": {
1550+
"variants": [
1551+
{
1552+
"name": "enabled",
1553+
"size": 1.0,
1554+
"range_end": 1.0,
1555+
"range_start": 0.0,
1556+
"emit_event_override": True
1557+
}
1558+
],
1559+
"experiment_version": 1,
1560+
"shuffle_version": 0,
1561+
"bucket_val": "user_id",
1562+
"log_bucketing": False
1563+
}
1564+
}
1565+
}
1566+
1567+
with create_temp_config_file(cfg) as f:
1568+
decider = setup_decider(f, self.dc, self.mock_span, self.event_logger)
1569+
1570+
self.assertEqual(self.event_logger.log.call_count, 0)
1571+
variant = decider.get_variant(experiment_name="feature_rollout_100")
1572+
self.assertEqual(variant, "enabled")
1573+
1574+
# FR does NOT emit exposure
1575+
self.assertEqual(self.event_logger.log.call_count, 0)
1576+
1577+
# FR is NOT "measured"
1578+
fr_cfg = decider.get_experiment(experiment_name="feature_rollout_100")
1579+
self.assertEqual(fr_cfg.measured, False)
1580+
1581+
variant = decider.get_variant(experiment_name="measured_rollout_100")
1582+
self.assertEqual(variant, "enabled")
1583+
1584+
# Measured Rollout DOES emit exposure (due to RV "emit_event_override" field)
1585+
self.assertEqual(self.event_logger.log.call_count, 1)
1586+
1587+
# MR IS "measured"
1588+
mr_cfg = decider.get_experiment(experiment_name="measured_rollout_100")
1589+
self.assertEqual(mr_cfg.measured, True)
1590+
15111591
def test_get_experiment(self):
15121592
with create_temp_config_file(self.exp_base_config) as f:
15131593
decider = setup_decider(f, self.dc, self.mock_span, self.event_logger)
@@ -1521,7 +1601,7 @@ def test_get_experiment(self):
15211601
self.assertEqual(experiment.bucket_val, cfg["experiment"]["bucket_val"])
15221602
self.assertEqual(experiment.start_ts, cfg["start_ts"])
15231603
self.assertEqual(experiment.stop_ts, cfg["stop_ts"])
1524-
self.assertEqual(experiment.owner, cfg["owner"])
1604+
self.assertEqual(experiment.owner, None)
15251605
self.assertEqual(experiment.emit_event, True)
15261606

15271607
def test_get_variant_without_expose_with_HG_as_control_1_and_child_returns_none_does_expose(

0 commit comments

Comments
 (0)