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 CHANGED
@@ -18,5 +18,5 @@ from .plugins import (
18
18
  )
19
19
 
20
20
  # x-release-please-start-version
21
- __version__ = "2.0.0"
21
+ __version__ = "2.1.1"
22
22
  # x-release-please-end
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 (type(attributeValue) is list):
227
+ if not type(conditionValue) is list:
197
228
  return False
198
- for cond in conditionValue:
199
- passing = False
200
- for attr in attributeValue:
201
- if evalConditionValue(cond, attr, savedGroups):
202
- passing = True
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
1
  Metadata-Version: 2.4
2
2
  Name: growthbook
3
- Version: 2.0.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,6 +1,6 @@
1
- growthbook/__init__.py,sha256=tksQ8wdB4p_iIIPCa1e5NsUie_NM-b1173zp5wkCGLQ,444
1
+ growthbook/__init__.py,sha256=fGRXWdeBsaPrFwuv84wVuuuafTILYbkv7EwyR9yKqa4,444
2
2
  growthbook/common_types.py,sha256=YKUmmYfzgrzLQ7kp2IPLc8QBA-B0QbnbF5viekNiTpw,15703
3
- growthbook/core.py,sha256=C1Nes_AiEuu6ypPghKuIeM2F22XUsLK1KrLW0xDwLYU,35963
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.0.0.dist-info/licenses/LICENSE,sha256=D-TcBckB0dTPUlNJ8jBiTIJIj1ekHLB1CY7HJtJKhMY,1069
12
- growthbook-2.0.0.dist-info/METADATA,sha256=yCwwaIc8N5XnwmAMNVcTtqN0ZPaFsbCtcgmgG-dD3wg,22726
13
- growthbook-2.0.0.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
14
- growthbook-2.0.0.dist-info/top_level.txt,sha256=dzfRQFGYejCIUstRSrrRVTMlxf7pBqASTI5S8gGRlXw,11
15
- growthbook-2.0.0.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any