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.
Files changed (27) hide show
  1. {growthbook-2.1.0/growthbook.egg-info → growthbook-2.1.1}/PKG-INFO +1 -1
  2. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/__init__.py +1 -1
  3. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/core.py +51 -11
  4. {growthbook-2.1.0 → growthbook-2.1.1/growthbook.egg-info}/PKG-INFO +1 -1
  5. {growthbook-2.1.0 → growthbook-2.1.1}/setup.cfg +1 -1
  6. {growthbook-2.1.0 → growthbook-2.1.1}/LICENSE +0 -0
  7. {growthbook-2.1.0 → growthbook-2.1.1}/MANIFEST.in +0 -0
  8. {growthbook-2.1.0 → growthbook-2.1.1}/README.md +0 -0
  9. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/common_types.py +0 -0
  10. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/growthbook.py +0 -0
  11. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/growthbook_client.py +0 -0
  12. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/plugins/__init__.py +0 -0
  13. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/plugins/base.py +0 -0
  14. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/plugins/growthbook_tracking.py +0 -0
  15. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/plugins/request_context.py +0 -0
  16. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook/py.typed +0 -0
  17. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook.egg-info/SOURCES.txt +0 -0
  18. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook.egg-info/dependency_links.txt +0 -0
  19. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook.egg-info/requires.txt +0 -0
  20. {growthbook-2.1.0 → growthbook-2.1.1}/growthbook.egg-info/top_level.txt +0 -0
  21. {growthbook-2.1.0 → growthbook-2.1.1}/pyproject.toml +0 -0
  22. {growthbook-2.1.0 → growthbook-2.1.1}/setup.py +0 -0
  23. {growthbook-2.1.0 → growthbook-2.1.1}/tests/conftest.py +0 -0
  24. {growthbook-2.1.0 → growthbook-2.1.1}/tests/test_etag.py +0 -0
  25. {growthbook-2.1.0 → growthbook-2.1.1}/tests/test_growthbook.py +0 -0
  26. {growthbook-2.1.0 → growthbook-2.1.1}/tests/test_growthbook_client.py +0 -0
  27. {growthbook-2.1.0 → growthbook-2.1.1}/tests/test_plugins.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: growthbook
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: Powerful Feature flagging and A/B testing for Python apps
5
5
  Home-page: https://github.com/growthbook/growthbook-python
6
6
  Author: GrowthBook
@@ -18,5 +18,5 @@ from .plugins import (
18
18
  )
19
19
 
20
20
  # x-release-please-start-version
21
- __version__ = "2.1.0"
21
+ __version__ = "2.1.1"
22
22
  # x-release-please-end
@@ -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 (type(attributeValue) is list):
227
+ if not type(conditionValue) is list:
215
228
  return False
216
- for cond in conditionValue:
217
- passing = False
218
- for attr in attributeValue:
219
- if evalConditionValue(cond, attr, savedGroups):
220
- passing = True
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",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: growthbook
3
- Version: 2.1.0
3
+ Version: 2.1.1
4
4
  Summary: Powerful Feature flagging and A/B testing for Python apps
5
5
  Home-page: https://github.com/growthbook/growthbook-python
6
6
  Author: GrowthBook
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 2.1.0
2
+ current_version = 2.1.1
3
3
  commit = True
4
4
  tag = True
5
5
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes