@@ -569,7 +569,7 @@ class GuildExperiment:
569
569
An experiment that blocks the rollout of this experiment.
570
570
aa_mode: :class:`bool`
571
571
Whether the experiment is in A/A mode.
572
- trigger_debugging:
572
+ trigger_debugging: :class:`bool`
573
573
Whether experiment analytics trigger debugging is enabled.
574
574
"""
575
575
@@ -776,9 +776,12 @@ class UserExperiment:
776
776
Whether the user has an explicit bucket override.
777
777
population: :class:`int`
778
778
The internal population group for the user, or None (-1) if manually overridden.
779
+ holdout: Optional[:class:`UserExperiment`]
780
+ An experiment that blocks the rollout of this experiment.
781
+ Only present if the user is in the holdout.
779
782
aa_mode: :class:`bool`
780
783
Whether the experiment is in A/A mode.
781
- trigger_debugging:
784
+ trigger_debugging: :class:`bool`
782
785
Whether experiment analytics trigger debugging is enabled.
783
786
"""
784
787
@@ -793,10 +796,11 @@ class UserExperiment:
793
796
'_result' ,
794
797
'aa_mode' ,
795
798
'trigger_debugging' ,
799
+ 'holdout' ,
796
800
)
797
801
798
802
def __init__ (self , * , state : ConnectionState , data : AssignmentPayload ):
799
- (hash , revision , bucket , override , population , hash_result , aa_mode , trigger_debugging , * _ ) = data
803
+ (hash , revision , bucket , override , population , hash_result , aa_mode , trigger_debugging , holdout_name , holdout_revision , holdout_bucket , * _ ) = data
800
804
801
805
self ._state = state
802
806
self ._name : Optional [str ] = None
@@ -809,6 +813,29 @@ def __init__(self, *, state: ConnectionState, data: AssignmentPayload):
809
813
self .aa_mode : bool = aa_mode == 1
810
814
self .trigger_debugging : bool = trigger_debugging == 1
811
815
816
+ self .holdout : Optional [UserExperiment ] = None
817
+ if holdout_name is not None :
818
+ holdout_hash = murmurhash32 (holdout_name , signed = False )
819
+ self .holdout = state .experiments .get (holdout_hash ) or UserExperiment .from_holdout (state , holdout_hash , holdout_name , holdout_revision , holdout_bucket )
820
+
821
+ @classmethod
822
+ def from_holdout (cls , state : ConnectionState , hash : int , name : str , revision : Optional [int ], bucket : Optional [int ]) -> UserExperiment :
823
+ self = cls .__new__ (cls )
824
+ self ._state = state
825
+ self ._name = name
826
+ self .hash = hash
827
+ self .revision = revision or 0
828
+ self .assignment = bucket or - 1
829
+
830
+ # Most of these fields are defaults
831
+ self .override = False
832
+ self .population = 0
833
+ self ._result = - 1
834
+ self .aa_mode = False
835
+ self .trigger_debugging = False
836
+ self .holdout = None
837
+ return self
838
+
812
839
def __repr__ (self ) -> str :
813
840
return f'<UserExperiment hash={ self .hash } { f" name={ self ._name !r} " if self ._name else "" } bucket={ self .bucket } >'
814
841
@@ -855,9 +882,11 @@ def result(self) -> int:
855
882
:exc:`ValueError`
856
883
The experiment name is unset without a precomputed result.
857
884
"""
858
- if self ._result :
885
+ if self ._result >= 0 :
859
886
return self ._result
860
887
elif not self .name :
861
888
raise ValueError ('The experiment name must be set to compute the result' )
862
889
else :
863
- return murmurhash32 (f'{ self .name } :{ self ._state .self_id } ' , signed = False ) % 10000
890
+ result = murmurhash32 (f'{ self .name } :{ self ._state .self_id } ' , signed = False ) % 10000
891
+ self ._result = result
892
+ return result
0 commit comments