growthbook 2.0.0__py2.py3-none-any.whl → 2.1.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- growthbook/__init__.py +1 -1
- growthbook/core.py +69 -11
- {growthbook-2.0.0.dist-info → growthbook-2.1.1.dist-info}/METADATA +1 -1
- {growthbook-2.0.0.dist-info → growthbook-2.1.1.dist-info}/RECORD +7 -7
- {growthbook-2.0.0.dist-info → growthbook-2.1.1.dist-info}/WHEEL +1 -1
- {growthbook-2.0.0.dist-info → growthbook-2.1.1.dist-info}/licenses/LICENSE +0 -0
- {growthbook-2.0.0.dist-info → growthbook-2.1.1.dist-info}/top_level.txt +0 -0
growthbook/__init__.py
CHANGED
growthbook/core.py
CHANGED
|
@@ -76,12 +76,17 @@ def getPath(attributes, path):
|
|
|
76
76
|
return None
|
|
77
77
|
return current
|
|
78
78
|
|
|
79
|
-
def evalConditionValue(conditionValue, attributeValue, savedGroups) -> bool:
|
|
79
|
+
def evalConditionValue(conditionValue, attributeValue, savedGroups, insensitive: bool = False) -> bool:
|
|
80
80
|
if type(conditionValue) is dict and isOperatorObject(conditionValue):
|
|
81
81
|
for key, value in conditionValue.items():
|
|
82
82
|
if not evalOperatorCondition(key, attributeValue, value, savedGroups):
|
|
83
83
|
return False
|
|
84
84
|
return True
|
|
85
|
+
|
|
86
|
+
# Simple equality comparison with optional case-insensitivity
|
|
87
|
+
if insensitive and type(conditionValue) is str and type(attributeValue) is str:
|
|
88
|
+
return conditionValue.lower() == attributeValue.lower()
|
|
89
|
+
|
|
85
90
|
return bool(conditionValue == attributeValue)
|
|
86
91
|
|
|
87
92
|
def elemMatch(condition, attributeValue, savedGroups) -> bool:
|
|
@@ -178,6 +183,24 @@ def evalOperatorCondition(operator, attributeValue, conditionValue, savedGroups)
|
|
|
178
183
|
return bool(r.search(attributeValue))
|
|
179
184
|
except Exception:
|
|
180
185
|
return False
|
|
186
|
+
elif operator == "$regexi":
|
|
187
|
+
try:
|
|
188
|
+
r = re.compile(conditionValue, re.IGNORECASE)
|
|
189
|
+
return bool(r.search(attributeValue))
|
|
190
|
+
except Exception:
|
|
191
|
+
return False
|
|
192
|
+
elif operator == "$notRegex":
|
|
193
|
+
try:
|
|
194
|
+
r = re.compile(conditionValue)
|
|
195
|
+
return not bool(r.search(attributeValue))
|
|
196
|
+
except Exception:
|
|
197
|
+
return False
|
|
198
|
+
elif operator == "$notRegexi":
|
|
199
|
+
try:
|
|
200
|
+
r = re.compile(conditionValue, re.IGNORECASE)
|
|
201
|
+
return not bool(r.search(attributeValue))
|
|
202
|
+
except Exception:
|
|
203
|
+
return False
|
|
181
204
|
elif operator == "$in":
|
|
182
205
|
if not type(conditionValue) is list:
|
|
183
206
|
return False
|
|
@@ -186,6 +209,14 @@ def evalOperatorCondition(operator, attributeValue, conditionValue, savedGroups)
|
|
|
186
209
|
if not type(conditionValue) is list:
|
|
187
210
|
return False
|
|
188
211
|
return not isIn(conditionValue, attributeValue)
|
|
212
|
+
elif operator == "$ini":
|
|
213
|
+
if not type(conditionValue) is list:
|
|
214
|
+
return False
|
|
215
|
+
return isIn(conditionValue, attributeValue, insensitive=True)
|
|
216
|
+
elif operator == "$nini":
|
|
217
|
+
if not type(conditionValue) is list:
|
|
218
|
+
return False
|
|
219
|
+
return not isIn(conditionValue, attributeValue, insensitive=True)
|
|
189
220
|
elif operator == "$elemMatch":
|
|
190
221
|
return elemMatch(conditionValue, attributeValue, savedGroups)
|
|
191
222
|
elif operator == "$size":
|
|
@@ -193,16 +224,13 @@ def evalOperatorCondition(operator, attributeValue, conditionValue, savedGroups)
|
|
|
193
224
|
return False
|
|
194
225
|
return evalConditionValue(conditionValue, len(attributeValue), savedGroups)
|
|
195
226
|
elif operator == "$all":
|
|
196
|
-
if not
|
|
227
|
+
if not type(conditionValue) is list:
|
|
197
228
|
return False
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if not passing:
|
|
204
|
-
return False
|
|
205
|
-
return True
|
|
229
|
+
return isInAll(conditionValue, attributeValue, savedGroups, insensitive=False)
|
|
230
|
+
elif operator == "$alli":
|
|
231
|
+
if not type(conditionValue) is list:
|
|
232
|
+
return False
|
|
233
|
+
return isInAll(conditionValue, attributeValue, savedGroups, insensitive=True)
|
|
206
234
|
elif operator == "$exists":
|
|
207
235
|
if not conditionValue:
|
|
208
236
|
return attributeValue is None
|
|
@@ -236,11 +264,41 @@ def paddedVersionString(input) -> str:
|
|
|
236
264
|
return "-".join([v.rjust(5, " ") if re.match(r"^[0-9]+$", v) else v for v in parts])
|
|
237
265
|
|
|
238
266
|
|
|
239
|
-
def isIn(conditionValue, attributeValue) -> bool:
|
|
267
|
+
def isIn(conditionValue, attributeValue, insensitive: bool = False) -> bool:
|
|
268
|
+
if insensitive:
|
|
269
|
+
# Helper function to case-fold values (lowercase for strings)
|
|
270
|
+
def case_fold(val):
|
|
271
|
+
return val.lower() if type(val) is str else val
|
|
272
|
+
|
|
273
|
+
# Do an intersection if attribute is an array (insensitive)
|
|
274
|
+
if type(attributeValue) is list:
|
|
275
|
+
return any(
|
|
276
|
+
case_fold(el) == case_fold(exp)
|
|
277
|
+
for el in attributeValue
|
|
278
|
+
for exp in conditionValue
|
|
279
|
+
)
|
|
280
|
+
return any(case_fold(attributeValue) == case_fold(exp) for exp in conditionValue)
|
|
281
|
+
|
|
282
|
+
# Case-sensitive behavior (original)
|
|
240
283
|
if type(attributeValue) is list:
|
|
241
284
|
return bool(set(conditionValue) & set(attributeValue))
|
|
242
285
|
return attributeValue in conditionValue
|
|
243
286
|
|
|
287
|
+
def isInAll(conditionValue, attributeValue, savedGroups, insensitive: bool = False) -> bool:
|
|
288
|
+
"""Check if attributeValue (array) contains all elements in conditionValue"""
|
|
289
|
+
if not type(attributeValue) is list:
|
|
290
|
+
return False
|
|
291
|
+
|
|
292
|
+
for cond in conditionValue:
|
|
293
|
+
passing = False
|
|
294
|
+
for attr in attributeValue:
|
|
295
|
+
if evalConditionValue(cond, attr, savedGroups, insensitive):
|
|
296
|
+
passing = True
|
|
297
|
+
break
|
|
298
|
+
if not passing:
|
|
299
|
+
return False
|
|
300
|
+
return True
|
|
301
|
+
|
|
244
302
|
def _getOrigHashValue(
|
|
245
303
|
eval_context: EvaluationContext,
|
|
246
304
|
attr: Optional[str] = "id",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
growthbook/__init__.py,sha256=
|
|
1
|
+
growthbook/__init__.py,sha256=fGRXWdeBsaPrFwuv84wVuuuafTILYbkv7EwyR9yKqa4,444
|
|
2
2
|
growthbook/common_types.py,sha256=YKUmmYfzgrzLQ7kp2IPLc8QBA-B0QbnbF5viekNiTpw,15703
|
|
3
|
-
growthbook/core.py,sha256=
|
|
3
|
+
growthbook/core.py,sha256=f23Dzo5itM3xK6WevkjllJ5rg1h8DgGuqWtldBOjpq4,38312
|
|
4
4
|
growthbook/growthbook.py,sha256=Ee6-jWPtvlgRvxRbsX-ZhrMV-8_F2T78Q7P30DVMsQM,47833
|
|
5
5
|
growthbook/growthbook_client.py,sha256=YnbKGbO2taWZXtlutuTf_ZSkL_WmhYLhr39fB6BFIcw,27578
|
|
6
6
|
growthbook/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -8,8 +8,8 @@ growthbook/plugins/__init__.py,sha256=y2eAV1sA041XWcftBVTDH0t-ggy9r2C5oKRYRF6XR6
|
|
|
8
8
|
growthbook/plugins/base.py,sha256=PWBXUBj62hi25Y5Eif9WmEWagWdkwGXHi2dMtn44bo8,3637
|
|
9
9
|
growthbook/plugins/growthbook_tracking.py,sha256=yN2xOHtRNsJuxkm16wY0YBQFxjEXDKnKcup7C9bQwe4,11351
|
|
10
10
|
growthbook/plugins/request_context.py,sha256=WzoGxalxPfrsN3RzfkvVYaUGat1A3N4AErnaS9IZ48Y,13005
|
|
11
|
-
growthbook-2.
|
|
12
|
-
growthbook-2.
|
|
13
|
-
growthbook-2.
|
|
14
|
-
growthbook-2.
|
|
15
|
-
growthbook-2.
|
|
11
|
+
growthbook-2.1.1.dist-info/licenses/LICENSE,sha256=D-TcBckB0dTPUlNJ8jBiTIJIj1ekHLB1CY7HJtJKhMY,1069
|
|
12
|
+
growthbook-2.1.1.dist-info/METADATA,sha256=xu4GvzLkoLNvb8jgtS3BnaRdvp32o_UFz3AcmGjjvxE,22726
|
|
13
|
+
growthbook-2.1.1.dist-info/WHEEL,sha256=Mk1ST5gDzEO5il5kYREiBnzzM469m5sI8ESPl7TRhJY,110
|
|
14
|
+
growthbook-2.1.1.dist-info/top_level.txt,sha256=dzfRQFGYejCIUstRSrrRVTMlxf7pBqASTI5S8gGRlXw,11
|
|
15
|
+
growthbook-2.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|