growthbook 2.1.0__tar.gz → 2.1.1__tar.gz
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-2.1.0/growthbook.egg-info → growthbook-2.1.1}/PKG-INFO +1 -1
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/__init__.py +1 -1
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/core.py +51 -11
- {growthbook-2.1.0 → growthbook-2.1.1/growthbook.egg-info}/PKG-INFO +1 -1
- {growthbook-2.1.0 → growthbook-2.1.1}/setup.cfg +1 -1
- {growthbook-2.1.0 → growthbook-2.1.1}/LICENSE +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/MANIFEST.in +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/README.md +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/common_types.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/growthbook.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/growthbook_client.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/plugins/__init__.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/plugins/base.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/plugins/growthbook_tracking.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/plugins/request_context.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/py.typed +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook.egg-info/SOURCES.txt +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook.egg-info/dependency_links.txt +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook.egg-info/requires.txt +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/growthbook.egg-info/top_level.txt +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/pyproject.toml +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/setup.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/tests/conftest.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/tests/test_etag.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/tests/test_growthbook.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/tests/test_growthbook_client.py +0 -0
- {growthbook-2.1.0 → growthbook-2.1.1}/tests/test_plugins.py +0 -0
|
@@ -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:
|
|
@@ -204,6 +209,14 @@ def evalOperatorCondition(operator, attributeValue, conditionValue, savedGroups)
|
|
|
204
209
|
if not type(conditionValue) is list:
|
|
205
210
|
return False
|
|
206
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)
|
|
207
220
|
elif operator == "$elemMatch":
|
|
208
221
|
return elemMatch(conditionValue, attributeValue, savedGroups)
|
|
209
222
|
elif operator == "$size":
|
|
@@ -211,16 +224,13 @@ def evalOperatorCondition(operator, attributeValue, conditionValue, savedGroups)
|
|
|
211
224
|
return False
|
|
212
225
|
return evalConditionValue(conditionValue, len(attributeValue), savedGroups)
|
|
213
226
|
elif operator == "$all":
|
|
214
|
-
if not
|
|
227
|
+
if not type(conditionValue) is list:
|
|
215
228
|
return False
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if not passing:
|
|
222
|
-
return False
|
|
223
|
-
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)
|
|
224
234
|
elif operator == "$exists":
|
|
225
235
|
if not conditionValue:
|
|
226
236
|
return attributeValue is None
|
|
@@ -254,11 +264,41 @@ def paddedVersionString(input) -> str:
|
|
|
254
264
|
return "-".join([v.rjust(5, " ") if re.match(r"^[0-9]+$", v) else v for v in parts])
|
|
255
265
|
|
|
256
266
|
|
|
257
|
-
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)
|
|
258
283
|
if type(attributeValue) is list:
|
|
259
284
|
return bool(set(conditionValue) & set(attributeValue))
|
|
260
285
|
return attributeValue in conditionValue
|
|
261
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
|
+
|
|
262
302
|
def _getOrigHashValue(
|
|
263
303
|
eval_context: EvaluationContext,
|
|
264
304
|
attr: Optional[str] = "id",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|