1
- # Copyright 2016-2017, 2019-2021 Optimizely
1
+ # Copyright 2016-2017, 2019-2022 Optimizely
2
2
# Licensed under the Apache License, Version 2.0 (the "License");
3
3
# you may not use this file except in compliance with the License.
4
4
# You may obtain a copy of the License at
11
11
# See the License for the specific language governing permissions and
12
12
# limitations under the License.
13
13
14
+ from __future__ import annotations
15
+ from typing import Optional , TYPE_CHECKING
14
16
import math
17
+ from sys import version_info
15
18
16
19
from .lib import pymmh3 as mmh3
17
20
18
21
19
- MAX_TRAFFIC_VALUE = 10000
20
- UNSIGNED_MAX_32_BIT_VALUE = 0xFFFFFFFF
21
- MAX_HASH_VALUE = math .pow (2 , 32 )
22
- HASH_SEED = 1
23
- BUCKETING_ID_TEMPLATE = '{bucketing_id}{parent_id}'
24
- GROUP_POLICIES = ['random' ]
22
+ if version_info < (3 , 8 ):
23
+ from typing_extensions import Final
24
+ else :
25
+ from typing import Final # type: ignore
26
+
27
+
28
+ if TYPE_CHECKING :
29
+ # prevent circular dependenacy by skipping import at runtime
30
+ from .project_config import ProjectConfig
31
+ from .entities import Experiment , Variation
32
+ from .helpers .types import TrafficAllocation
33
+
34
+
35
+ MAX_TRAFFIC_VALUE : Final = 10000
36
+ UNSIGNED_MAX_32_BIT_VALUE : Final = 0xFFFFFFFF
37
+ MAX_HASH_VALUE : Final = math .pow (2 , 32 )
38
+ HASH_SEED : Final = 1
39
+ BUCKETING_ID_TEMPLATE : Final = '{bucketing_id}{parent_id}'
40
+ GROUP_POLICIES : Final = ['random' ]
25
41
26
42
27
43
class Bucketer :
28
44
""" Optimizely bucketing algorithm that evenly distributes visitors. """
29
45
30
- def __init__ (self ):
46
+ def __init__ (self ) -> None :
31
47
""" Bucketer init method to set bucketing seed and logger instance. """
32
48
33
49
self .bucket_seed = HASH_SEED
34
50
35
- def _generate_unsigned_hash_code_32_bit (self , bucketing_id ) :
51
+ def _generate_unsigned_hash_code_32_bit (self , bucketing_id : str ) -> int :
36
52
""" Helper method to retrieve hash code.
37
53
38
54
Args:
@@ -45,7 +61,7 @@ def _generate_unsigned_hash_code_32_bit(self, bucketing_id):
45
61
# Adjusting MurmurHash code to be unsigned
46
62
return mmh3 .hash (bucketing_id , self .bucket_seed ) & UNSIGNED_MAX_32_BIT_VALUE
47
63
48
- def _generate_bucket_value (self , bucketing_id ) :
64
+ def _generate_bucket_value (self , bucketing_id : str ) -> int :
49
65
""" Helper function to generate bucket value in half-closed interval [0, MAX_TRAFFIC_VALUE).
50
66
51
67
Args:
@@ -58,7 +74,10 @@ def _generate_bucket_value(self, bucketing_id):
58
74
ratio = float (self ._generate_unsigned_hash_code_32_bit (bucketing_id )) / MAX_HASH_VALUE
59
75
return math .floor (ratio * MAX_TRAFFIC_VALUE )
60
76
61
- def find_bucket (self , project_config , bucketing_id , parent_id , traffic_allocations ):
77
+ def find_bucket (
78
+ self , project_config : ProjectConfig , bucketing_id : str ,
79
+ parent_id : Optional [str ], traffic_allocations : list [TrafficAllocation ]
80
+ ) -> Optional [str ]:
62
81
""" Determine entity based on bucket value and traffic allocations.
63
82
64
83
Args:
@@ -78,12 +97,15 @@ def find_bucket(self, project_config, bucketing_id, parent_id, traffic_allocatio
78
97
79
98
for traffic_allocation in traffic_allocations :
80
99
current_end_of_range = traffic_allocation .get ('endOfRange' )
81
- if bucketing_number < current_end_of_range :
100
+ if current_end_of_range is not None and bucketing_number < current_end_of_range :
82
101
return traffic_allocation .get ('entityId' )
83
102
84
103
return None
85
104
86
- def bucket (self , project_config , experiment , user_id , bucketing_id ):
105
+ def bucket (
106
+ self , project_config : ProjectConfig ,
107
+ experiment : Experiment , user_id : str , bucketing_id : str
108
+ ) -> tuple [Optional [Variation ], list [str ]]:
87
109
""" For a given experiment and bucketing ID determines variation to be shown to user.
88
110
89
111
Args:
@@ -97,7 +119,7 @@ def bucket(self, project_config, experiment, user_id, bucketing_id):
97
119
and array of log messages representing decision making.
98
120
*/.
99
121
"""
100
- decide_reasons = []
122
+ decide_reasons : list [ str ] = []
101
123
if not experiment :
102
124
return None , decide_reasons
103
125
0 commit comments