flagsmith-flag-engine 10.0.2__tar.gz → 10.0.4__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 (29) hide show
  1. {flagsmith_flag_engine-10.0.2/flagsmith_flag_engine.egg-info → flagsmith_flag_engine-10.0.4}/PKG-INFO +1 -1
  2. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/segments/evaluator.py +47 -31
  3. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4/flagsmith_flag_engine.egg-info}/PKG-INFO +1 -1
  4. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/setup.py +1 -1
  5. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/LICENSE.txt +0 -0
  6. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/README.md +0 -0
  7. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/__init__.py +0 -0
  8. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/context/__init__.py +0 -0
  9. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/context/mappers.py +0 -0
  10. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/context/types.py +0 -0
  11. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/engine.py +0 -0
  12. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/py.typed +0 -0
  13. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/result/__init__.py +0 -0
  14. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/result/types.py +0 -0
  15. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/segments/__init__.py +0 -0
  16. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/segments/constants.py +0 -0
  17. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/segments/types.py +0 -0
  18. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/segments/utils.py +0 -0
  19. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/utils/__init__.py +0 -0
  20. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/utils/hashing.py +0 -0
  21. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/utils/json/__init__.py +0 -0
  22. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/utils/json/encoders.py +0 -0
  23. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/utils/semver.py +0 -0
  24. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flag_engine/utils/types.py +0 -0
  25. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flagsmith_flag_engine.egg-info/SOURCES.txt +0 -0
  26. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flagsmith_flag_engine.egg-info/dependency_links.txt +0 -0
  27. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flagsmith_flag_engine.egg-info/requires.txt +0 -0
  28. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/flagsmith_flag_engine.egg-info/top_level.txt +0 -0
  29. {flagsmith_flag_engine-10.0.2 → flagsmith_flag_engine-10.0.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flagsmith-flag-engine
3
- Version: 10.0.2
3
+ Version: 10.0.4
4
4
  Summary: Flag engine for the Flagsmith API.
5
5
  Home-page: https://github.com/Flagsmith/flagsmith-engine
6
6
  Author: Flagsmith
@@ -264,53 +264,45 @@ def context_matches_condition(
264
264
  condition: SegmentCondition,
265
265
  segment_key: SupportsStr,
266
266
  ) -> bool:
267
- context_value = (
268
- get_context_value(context, condition_property)
269
- if (condition_property := condition.get("property"))
270
- else None
271
- )
267
+ context_value: ContextValue
268
+ condition_property = condition["property"]
269
+ condition_operator = condition["operator"]
270
+
271
+ if condition_operator == constants.PERCENTAGE_SPLIT and (not condition_property):
272
+ # Currently, the only supported condition with a blank property
273
+ # is percentage split.
274
+ # In this case, we use the identity key as context value.
275
+ # This is mainly to support legacy segments created before
276
+ # we introduced JSONPath support.
277
+ context_value = _get_identity_key(context)
278
+ else:
279
+ context_value = get_context_value(context, condition_property)
272
280
 
273
- if condition["operator"] == constants.IN:
274
- if isinstance(segment_value := condition["value"], list):
275
- in_values = segment_value
276
- else:
277
- try:
278
- in_values = json.loads(segment_value)
279
- # Only accept JSON lists.
280
- # Ideally, we should use something like pydantic.TypeAdapter[list[str]],
281
- # but we aim to ditch the pydantic dependency in the future.
282
- if not isinstance(in_values, list):
283
- raise ValueError
284
- except ValueError:
285
- in_values = segment_value.split(",")
286
- in_values = [str(value) for value in in_values]
281
+ if condition_operator == constants.IN:
282
+ in_values = _get_in_values(condition["value"])
287
283
  # Guard against comparing boolean values to numeric strings.
288
- if isinstance(context_value, int) and not (
289
- context_value is True or context_value is False
290
- ):
284
+ if type(context_value) is int:
291
285
  context_value = str(context_value)
292
286
  return context_value in in_values
293
287
 
294
288
  condition = typing.cast(StrValueSegmentCondition, condition)
295
289
 
296
- if condition["operator"] == constants.PERCENTAGE_SPLIT:
297
- if context_value is not None:
298
- object_ids = [segment_key, context_value]
299
- elif identity_key := _get_identity_key(context):
300
- object_ids = [segment_key, identity_key]
301
- else:
290
+ if condition_operator == constants.PERCENTAGE_SPLIT:
291
+ if context_value is None:
302
292
  return False
303
293
 
294
+ object_ids = [segment_key, context_value]
295
+
304
296
  try:
305
297
  float_value = float(condition["value"])
306
298
  except ValueError:
307
299
  return False
308
300
  return get_hashed_percentage_for_object_ids(object_ids) <= float_value
309
301
 
310
- if condition["operator"] == constants.IS_NOT_SET:
302
+ if condition_operator == constants.IS_NOT_SET:
311
303
  return context_value is None
312
304
 
313
- if condition["operator"] == constants.IS_SET:
305
+ if condition_operator == constants.IS_SET:
314
306
  return context_value is not None
315
307
 
316
308
  return (
@@ -342,6 +334,30 @@ def _matches_context_value(
342
334
  return False
343
335
 
344
336
 
337
+ @lru_cache(maxsize=1024)
338
+ def _parse_in_values_str(segment_value: str) -> frozenset[str]:
339
+ """
340
+ Parse a string-form IN condition value into a frozenset of strings.
341
+ A bracketed value is tried as JSON first (with CSV fallback on parse
342
+ error); anything else is split on commas directly.
343
+ """
344
+ if segment_value.startswith("["):
345
+ try:
346
+ parsed: list[typing.Any] = json.loads(segment_value)
347
+ except ValueError:
348
+ return frozenset(segment_value.split(","))
349
+ return frozenset(v if type(v) is str else str(v) for v in parsed)
350
+ return frozenset(segment_value.split(","))
351
+
352
+
353
+ def _get_in_values(
354
+ segment_value: typing.Union[str, list[typing.Any]],
355
+ ) -> frozenset[str]:
356
+ if isinstance(segment_value, list):
357
+ return frozenset(v if type(v) is str else str(v) for v in segment_value)
358
+ return _parse_in_values_str(segment_value)
359
+
360
+
345
361
  def _evaluate_not_contains(
346
362
  segment_value: typing.Optional[str],
347
363
  context_value: ContextValue,
@@ -417,7 +433,7 @@ MATCHERS_BY_OPERATOR: dict[
417
433
 
418
434
  def _get_identity_key(
419
435
  context: _EvaluationContextAnyMeta,
420
- ) -> typing.Optional[SupportsStr]:
436
+ ) -> typing.Optional[str]:
421
437
  if identity_context := context.get("identity"):
422
438
  return identity_context.get("key")
423
439
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flagsmith-flag-engine
3
- Version: 10.0.2
3
+ Version: 10.0.4
4
4
  Summary: Flag engine for the Flagsmith API.
5
5
  Home-page: https://github.com/Flagsmith/flagsmith-engine
6
6
  Author: Flagsmith
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
2
2
 
3
3
  setup(
4
4
  name="flagsmith-flag-engine",
5
- version="10.0.2",
5
+ version="10.0.4",
6
6
  author="Flagsmith",
7
7
  author_email="support@flagsmith.com",
8
8
  packages=find_packages(include=["flag_engine", "flag_engine.*"]),