44
44
FlagsAndPayloads ,
45
45
FlagsResponse ,
46
46
FlagValue ,
47
+ SendFeatureFlagsOptions ,
47
48
normalize_flags_response ,
48
49
to_flags_and_payloads ,
49
50
to_payloads ,
@@ -313,6 +314,7 @@ def get_feature_variants(
313
314
person_properties = None ,
314
315
group_properties = None ,
315
316
disable_geoip = None ,
317
+ flag_keys_to_evaluate : Optional [list [str ]] = None ,
316
318
) -> dict [str , Union [bool , str ]]:
317
319
"""
318
320
Get feature flag variants for a user by calling decide.
@@ -323,12 +325,19 @@ def get_feature_variants(
323
325
person_properties: A dictionary of person properties.
324
326
group_properties: A dictionary of group properties.
325
327
disable_geoip: Whether to disable GeoIP for this request.
328
+ flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
329
+ only these flags will be evaluated, improving performance.
326
330
327
331
Category:
328
332
Feature Flags
329
333
"""
330
334
resp_data = self .get_flags_decision (
331
- distinct_id , groups , person_properties , group_properties , disable_geoip
335
+ distinct_id ,
336
+ groups ,
337
+ person_properties ,
338
+ group_properties ,
339
+ disable_geoip ,
340
+ flag_keys_to_evaluate ,
332
341
)
333
342
return to_values (resp_data ) or {}
334
343
@@ -339,6 +348,7 @@ def get_feature_payloads(
339
348
person_properties = None ,
340
349
group_properties = None ,
341
350
disable_geoip = None ,
351
+ flag_keys_to_evaluate : Optional [list [str ]] = None ,
342
352
) -> dict [str , str ]:
343
353
"""
344
354
Get feature flag payloads for a user by calling decide.
@@ -349,6 +359,8 @@ def get_feature_payloads(
349
359
person_properties: A dictionary of person properties.
350
360
group_properties: A dictionary of group properties.
351
361
disable_geoip: Whether to disable GeoIP for this request.
362
+ flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
363
+ only these flags will be evaluated, improving performance.
352
364
353
365
Examples:
354
366
```python
@@ -359,7 +371,12 @@ def get_feature_payloads(
359
371
Feature Flags
360
372
"""
361
373
resp_data = self .get_flags_decision (
362
- distinct_id , groups , person_properties , group_properties , disable_geoip
374
+ distinct_id ,
375
+ groups ,
376
+ person_properties ,
377
+ group_properties ,
378
+ disable_geoip ,
379
+ flag_keys_to_evaluate ,
363
380
)
364
381
return to_payloads (resp_data ) or {}
365
382
@@ -370,6 +387,7 @@ def get_feature_flags_and_payloads(
370
387
person_properties = None ,
371
388
group_properties = None ,
372
389
disable_geoip = None ,
390
+ flag_keys_to_evaluate : Optional [list [str ]] = None ,
373
391
) -> FlagsAndPayloads :
374
392
"""
375
393
Get feature flags and payloads for a user by calling decide.
@@ -380,6 +398,8 @@ def get_feature_flags_and_payloads(
380
398
person_properties: A dictionary of person properties.
381
399
group_properties: A dictionary of group properties.
382
400
disable_geoip: Whether to disable GeoIP for this request.
401
+ flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
402
+ only these flags will be evaluated, improving performance.
383
403
384
404
Examples:
385
405
```python
@@ -390,7 +410,12 @@ def get_feature_flags_and_payloads(
390
410
Feature Flags
391
411
"""
392
412
resp = self .get_flags_decision (
393
- distinct_id , groups , person_properties , group_properties , disable_geoip
413
+ distinct_id ,
414
+ groups ,
415
+ person_properties ,
416
+ group_properties ,
417
+ disable_geoip ,
418
+ flag_keys_to_evaluate ,
394
419
)
395
420
return to_flags_and_payloads (resp )
396
421
@@ -401,6 +426,7 @@ def get_flags_decision(
401
426
person_properties = None ,
402
427
group_properties = None ,
403
428
disable_geoip = None ,
429
+ flag_keys_to_evaluate : Optional [list [str ]] = None ,
404
430
) -> FlagsResponse :
405
431
"""
406
432
Get feature flags decision.
@@ -411,6 +437,8 @@ def get_flags_decision(
411
437
person_properties: A dictionary of person properties.
412
438
group_properties: A dictionary of group properties.
413
439
disable_geoip: Whether to disable GeoIP for this request.
440
+ flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
441
+ only these flags will be evaluated, improving performance.
414
442
415
443
Examples:
416
444
```python
@@ -441,6 +469,9 @@ def get_flags_decision(
441
469
"geoip_disable" : disable_geoip ,
442
470
}
443
471
472
+ if flag_keys_to_evaluate :
473
+ request_data ["flag_keys_to_evaluate" ] = flag_keys_to_evaluate
474
+
444
475
resp_data = flags (
445
476
self .api_key ,
446
477
self .host ,
@@ -545,6 +576,7 @@ def capture(
545
576
group_properties = flag_options ["group_properties" ],
546
577
disable_geoip = disable_geoip ,
547
578
only_evaluate_locally = True ,
579
+ flag_keys_to_evaluate = flag_options ["flag_keys_filter" ],
548
580
)
549
581
else :
550
582
# Default behavior - use remote evaluation
@@ -554,6 +586,7 @@ def capture(
554
586
person_properties = flag_options ["person_properties" ],
555
587
group_properties = flag_options ["group_properties" ],
556
588
disable_geoip = disable_geoip ,
589
+ flag_keys_to_evaluate = flag_options ["flag_keys_filter" ],
557
590
)
558
591
except Exception as e :
559
592
self .log .exception (
@@ -586,16 +619,16 @@ def capture(
586
619
587
620
return self ._enqueue (msg , disable_geoip )
588
621
589
- def _parse_send_feature_flags (self , send_feature_flags ) -> dict :
622
+ def _parse_send_feature_flags (self , send_feature_flags ) -> SendFeatureFlagsOptions :
590
623
"""
591
624
Parse and normalize send_feature_flags parameter into a standard format.
592
625
593
626
Args:
594
627
send_feature_flags: Either bool or SendFeatureFlagsOptions dict
595
628
596
629
Returns:
597
- dict : Normalized options with keys: should_send, only_evaluate_locally,
598
- person_properties, group_properties
630
+ SendFeatureFlagsOptions : Normalized options with keys: should_send, only_evaluate_locally,
631
+ person_properties, group_properties, flag_keys_filter
599
632
600
633
Raises:
601
634
TypeError: If send_feature_flags is not bool or dict
@@ -608,13 +641,15 @@ def _parse_send_feature_flags(self, send_feature_flags) -> dict:
608
641
),
609
642
"person_properties" : send_feature_flags .get ("person_properties" ),
610
643
"group_properties" : send_feature_flags .get ("group_properties" ),
644
+ "flag_keys_filter" : send_feature_flags .get ("flag_keys_filter" ),
611
645
}
612
646
elif isinstance (send_feature_flags , bool ):
613
647
return {
614
648
"should_send" : send_feature_flags ,
615
649
"only_evaluate_locally" : None ,
616
650
"person_properties" : None ,
617
651
"group_properties" : None ,
652
+ "flag_keys_filter" : None ,
618
653
}
619
654
else :
620
655
raise TypeError (
@@ -1184,12 +1219,12 @@ def _compute_flag_locally(
1184
1219
self .log .warning (
1185
1220
f"[FEATURE FLAGS] Unknown group type index { aggregation_group_type_index } for feature flag { feature_flag ['key' ]} "
1186
1221
)
1187
- # failover to `/decide/ `
1222
+ # failover to `/flags `
1188
1223
raise InconclusiveMatchError ("Flag has unknown group type index" )
1189
1224
1190
1225
if group_name not in groups :
1191
1226
# Group flags are never enabled in `groups` aren't passed in
1192
- # don't failover to `/decide/ `, since response will be the same
1227
+ # don't failover to `/flags `, since response will be the same
1193
1228
if warn_on_unknown_groups :
1194
1229
self .log .warning (
1195
1230
f"[FEATURE FLAGS] Can't compute group feature flag: { feature_flag ['key' ]} without group names passed in"
@@ -1317,7 +1352,7 @@ def _get_feature_flag_result(
1317
1352
)
1318
1353
elif not only_evaluate_locally :
1319
1354
try :
1320
- flag_details , request_id = self ._get_feature_flag_details_from_decide (
1355
+ flag_details , request_id = self ._get_feature_flag_details_from_server (
1321
1356
key ,
1322
1357
distinct_id ,
1323
1358
groups ,
@@ -1557,7 +1592,7 @@ def get_feature_flag_payload(
1557
1592
)
1558
1593
return feature_flag_result .payload if feature_flag_result else None
1559
1594
1560
- def _get_feature_flag_details_from_decide (
1595
+ def _get_feature_flag_details_from_server (
1561
1596
self ,
1562
1597
key : str ,
1563
1598
distinct_id : ID_TYPES ,
@@ -1567,10 +1602,15 @@ def _get_feature_flag_details_from_decide(
1567
1602
disable_geoip : Optional [bool ],
1568
1603
) -> tuple [Optional [FeatureFlag ], Optional [str ]]:
1569
1604
"""
1570
- Calls /decide and returns the flag details and request id
1605
+ Calls /flags and returns the flag details and request id
1571
1606
"""
1572
1607
resp_data = self .get_flags_decision (
1573
- distinct_id , groups , person_properties , group_properties , disable_geoip
1608
+ distinct_id ,
1609
+ groups ,
1610
+ person_properties ,
1611
+ group_properties ,
1612
+ disable_geoip ,
1613
+ flag_keys_to_evaluate = [key ],
1574
1614
)
1575
1615
request_id = resp_data .get ("requestId" )
1576
1616
flags = resp_data .get ("flags" )
@@ -1686,6 +1726,7 @@ def get_all_flags(
1686
1726
group_properties = None ,
1687
1727
only_evaluate_locally = False ,
1688
1728
disable_geoip = None ,
1729
+ flag_keys_to_evaluate : Optional [list [str ]] = None ,
1689
1730
) -> Optional [dict [str , Union [bool , str ]]]:
1690
1731
"""
1691
1732
Get all feature flags for a user.
@@ -1697,6 +1738,8 @@ def get_all_flags(
1697
1738
group_properties: A dictionary of group properties.
1698
1739
only_evaluate_locally: Whether to only evaluate locally.
1699
1740
disable_geoip: Whether to disable GeoIP for this request.
1741
+ flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
1742
+ only these flags will be evaluated, improving performance.
1700
1743
1701
1744
Examples:
1702
1745
```python
@@ -1713,6 +1756,7 @@ def get_all_flags(
1713
1756
group_properties = group_properties ,
1714
1757
only_evaluate_locally = only_evaluate_locally ,
1715
1758
disable_geoip = disable_geoip ,
1759
+ flag_keys_to_evaluate = flag_keys_to_evaluate ,
1716
1760
)
1717
1761
1718
1762
return response ["featureFlags" ]
@@ -1726,6 +1770,7 @@ def get_all_flags_and_payloads(
1726
1770
group_properties = None ,
1727
1771
only_evaluate_locally = False ,
1728
1772
disable_geoip = None ,
1773
+ flag_keys_to_evaluate : Optional [list [str ]] = None ,
1729
1774
) -> FlagsAndPayloads :
1730
1775
"""
1731
1776
Get all feature flags and their payloads for a user.
@@ -1737,6 +1782,8 @@ def get_all_flags_and_payloads(
1737
1782
group_properties: A dictionary of group properties.
1738
1783
only_evaluate_locally: Whether to only evaluate locally.
1739
1784
disable_geoip: Whether to disable GeoIP for this request.
1785
+ flag_keys_to_evaluate: A list of specific flag keys to evaluate. If provided,
1786
+ only these flags will be evaluated, improving performance.
1740
1787
1741
1788
Examples:
1742
1789
```python
@@ -1760,6 +1807,7 @@ def get_all_flags_and_payloads(
1760
1807
groups = groups ,
1761
1808
person_properties = person_properties ,
1762
1809
group_properties = group_properties ,
1810
+ flag_keys_to_evaluate = flag_keys_to_evaluate ,
1763
1811
)
1764
1812
1765
1813
if fallback_to_decide and not only_evaluate_locally :
@@ -1770,6 +1818,7 @@ def get_all_flags_and_payloads(
1770
1818
person_properties = person_properties ,
1771
1819
group_properties = group_properties ,
1772
1820
disable_geoip = disable_geoip ,
1821
+ flag_keys_to_evaluate = flag_keys_to_evaluate ,
1773
1822
)
1774
1823
return to_flags_and_payloads (decide_response )
1775
1824
except Exception as e :
@@ -1787,6 +1836,7 @@ def _get_all_flags_and_payloads_locally(
1787
1836
person_properties = None ,
1788
1837
group_properties = None ,
1789
1838
warn_on_unknown_groups = False ,
1839
+ flag_keys_to_evaluate : Optional [list [str ]] = None ,
1790
1840
) -> tuple [FlagsAndPayloads , bool ]:
1791
1841
person_properties = person_properties or {}
1792
1842
group_properties = group_properties or {}
@@ -1799,7 +1849,15 @@ def _get_all_flags_and_payloads_locally(
1799
1849
fallback_to_decide = False
1800
1850
# If loading in previous line failed
1801
1851
if self .feature_flags :
1802
- for flag in self .feature_flags :
1852
+ # Filter flags based on flag_keys_to_evaluate if provided
1853
+ flags_to_process = self .feature_flags
1854
+ if flag_keys_to_evaluate :
1855
+ flag_keys_set = set (flag_keys_to_evaluate )
1856
+ flags_to_process = [
1857
+ flag for flag in self .feature_flags if flag ["key" ] in flag_keys_set
1858
+ ]
1859
+
1860
+ for flag in flags_to_process :
1803
1861
try :
1804
1862
flags [flag ["key" ]] = self ._compute_flag_locally (
1805
1863
flag ,
@@ -1815,7 +1873,7 @@ def _get_all_flags_and_payloads_locally(
1815
1873
if matched_payload is not None :
1816
1874
payloads [flag ["key" ]] = matched_payload
1817
1875
except InconclusiveMatchError :
1818
- # No need to log this, since it's just telling us to fall back to `/decide `
1876
+ # No need to log this, since it's just telling us to fall back to `/flags `
1819
1877
fallback_to_decide = True
1820
1878
except Exception as e :
1821
1879
self .log .exception (
0 commit comments